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

如何从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 07:24:12