You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Zod+TypeScript:提取z.object特定键值到列表的类型错误排查

问题分析与解决

问题原因

当泛型参数T是多个Zod Schema的联合类型时,TypeScript对z.infer<T>["items"]的类型解析会出现偏差:它会将所有Schema对应的items类型合并为交叉类型,而非预期的联合类型。这是TypeScript的设计行为——对联合类型进行索引访问时,若所有成员都包含该属性,TypeScript会返回这些属性类型的交叉,以此保证类型安全(理论上要满足所有联合成员的类型约束),但这和我们实际需要的“每个Schema对应自己的items类型”的需求冲突,导致push元素时出现类型不匹配错误。

解决办法

方法1:用分发条件类型提取正确的联合类型

通过自定义类型工具,利用TypeScript的条件类型分发特性,从联合Schema中提取出对应的items元素类型的联合:

import { z } from "zod";

// 示例Schema
const SchemaA = z.object({ items: z.array(z.string()) });
const SchemaB = z.object({ items: z.array(z.number()) });
type PossibleSchemas = typeof SchemaA | typeof SchemaB;

// 提取单个Schema对应的items元素类型
type ExtractItemType<T extends z.ZodType<any, any, { items: any[] }>> = z.infer<T>["items"][number];

// 对联合Schema进行分发,得到items元素的联合类型
type ExtractItemsUnion<T> = T extends z.ZodType<any, any, { items: any[] }> ? ExtractItemType<T> : never;

function lookDeep<T extends PossibleSchemas>(schema: T, data: unknown) {
  const valid = schema.parse(data);
  // 明确extractedItems的类型为联合类型数组
  const extractedItems: ExtractItemsUnion<T>[] = [];
  
  // 用类型断言告诉TypeScript当前item属于联合类型的一员
  valid.items.forEach(item => extractedItems.push(item as ExtractItemsUnion<T>));
  
  return extractedItems;
}

方法2:拆分函数,处理单个Schema而非联合

将通用函数拆分为处理单个Schema的基础函数,再针对联合Schema做分支判断,避免TypeScript对联合类型的索引解析出错:

import { z } from "zod";

const SchemaA = z.object({ items: z.array(z.string()) });
const SchemaB = z.object({ items: z.array(z.number()) });

// 基础函数:仅处理单个带有items数组的Zod Schema
function lookDeepSingle<T extends z.ZodType<any, any, { items: any[] }>>(schema: T, data: unknown) {
  const valid = schema.parse(data);
  const extractedItems: z.infer<T>["items"][number][] = [];
  valid.items.forEach(item => extractedItems.push(item));
  return extractedItems;
}

// 对外暴露的函数:处理联合Schema的情况
function lookDeep(schema: typeof SchemaA | typeof SchemaB, data: unknown) {
  if (schema === SchemaA) {
    return lookDeepSingle(schema, data);
  }
  return lookDeepSingle(schema, data);
}

方法3:类型断言(不推荐,仅在确定类型安全时使用)

如果能确保当前处理的item类型和目标数组类型匹配,可以直接用类型断言跳过检查:

valid.items.forEach(item => extractedItems.push(item as any));

注意:这种方式会丢失TypeScript的类型检查,可能引入潜在的类型错误,仅作为临时解决方案。

内容的提问来源于stack exchange,提问作者Nate

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 08:43:39