如何从Zod嵌套对象数组中提取指定键的类型?
Zod嵌套数组对象的类型提取与复用方案
问题说明
抽象场景的问题
当尝试从Zod对象中提取嵌套的数组字段时,直接使用pick方法得到的仍是ZodObject类型,无法直接访问数组元素的Schema:
const obj = z.object({ nestedArray: z.array(z.object({ valueIWant: z.string() })) }) // 结果是ZodObject类型,而非期望的ZodArray const arrayOfObjs = obj.pick({ nestedArray: true }) // 此处会报错,因为arrayOfObjs是ZodObject,没有element属性 arrayOfObjs.element.pick({ valueIWant: true })
直接操作数组的正确示例
如果直接定义Zod数组,就可以通过.element访问数组元素的Schema:
// 类型为ZodArray const arr = z.array(z.object({ valueIWant: z.string() })) // 正确提取元素内指定字段的Schema const myValue = arr.element.pick({ valueIWant: true })
实际业务场景需求
现有一个API返回结构的Zod Schema:
export const wordAPI = z.object({ words: z.array( z.object({ id: z.string(), word: z.string(), translation: z.string(), type: z.enum(['verb', 'adjective', 'noun']) }) ) })
在tRPC的接口输入中,需要支持按单词类型过滤,但目前只能重复编写枚举z.enum(['verb', 'adjective', 'noun']),容易引发维护问题,希望从已有Schema中推导该枚举类型:
export const translationsRouter = createRouter().query('get', { input: z.object({ limit: z.number().default(10), avoid: z.array(z.string()).nullish(), wordType: z.enum(['verb', 'adjective', 'noun']).nullish() // 需要替换为推导的类型 }), // ...其他逻辑 })
解决方案
方案1:拆分Schema,从根源复用
最简洁且易维护的方式是将数组内的对象Schema单独提取,后续所有需要用到的地方直接复用:
// 单独定义单词对象的Schema const wordSchema = z.object({ id: z.string(), word: z.string(), translation: z.string(), type: z.enum(['verb', 'adjective', 'noun']) }) // 复用wordSchema定义API返回结构 export const wordAPI = z.object({ words: z.array(wordSchema) }) // 在tRPC输入中直接复用wordSchema中的type字段 export const translationsRouter = createRouter().query('get', { input: z.object({ limit: z.number().default(10), avoid: z.array(z.string()).nullish(), wordType: wordSchema.shape.type.nullish() }), // ...其他逻辑 })
这种方式只需要在一处维护枚举值,后续修改时无需多处同步,降低出错概率。
方案2:从已有嵌套Schema中提取
如果不想拆分原有的Schema,可以通过Zod的shape属性逐层获取到目标字段的Schema:
export const wordAPI = z.object({ words: z.array( z.object({ id: z.string(), word: z.string(), translation: z.string(), type: z.enum(['verb', 'adjective', 'noun']) }) ) }) // 从wordAPI中提取数组元素的Schema const wordElementSchema = wordAPI.shape.words.element // 在tRPC输入中复用提取到的枚举Schema export const translationsRouter = createRouter().query('get', { input: z.object({ limit: z.number().default(10), avoid: z.array(z.string()).nullish(), wordType: wordElementSchema.shape.type.nullish() }), // ...其他逻辑 })
这里的关键是:通过wordAPI.shape.words拿到数组的ZodArray类型,再通过.element获取数组内对象的Schema,最后用.shape.type拿到对应的枚举Schema。
补充说明
之前使用pick方法失败的原因是:obj.pick({ nestedArray: true })返回的是包含nestedArray字段的ZodObject,而非ZodArray类型。正确的做法是通过obj.shape.nestedArray直接获取数组的Schema,再访问其.element属性。
内容的提问来源于stack exchange,提问作者Herbie Vine
相关产品推荐
相关产品推荐

