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

如何递归为Zod Schema应用.passthrough()或修改默认行为?

问题描述

我正在对接一个返回复杂嵌套数据结构且频繁变更(新增数据等)的API。我希望验证API返回的最小属性集,同时保留所有其他属性(代码需读取、更新并回传这些数据),因此在Zod Schema中使用了.passthrough()方法。

该方法运行正常,但需要逐个为每个内部Schema调用.passthrough()才能避免丢失额外属性,示例代码如下:

/* sample data*/
const someObjectFromAnApi = JSON.parse(`{
  "coords": { "x": 42, "y": 0, "z": 30 },
  "color": { "r": 20, "g": 30, "b": 30, "alpha": 0.5 },
  "description": "This is here"
}`);
/* /sample data*/

const dataSchema = z
  .object({
    coords: z
      .object({
        x: z.number(),
        y: z.number()
// 不需要验证`z`属性
      })
      .passthrough(), // 第一个passthrough
    color: z
      .object({
        r: z.number(),
        g: z.number(),
        b: z.number()
// 不需要验证`alpha`属性
      })
      .passthrough() // 第二个passthrough
  })
// 不需要验证`description`属性
  .passthrough(); //第三个passthrough

我的问题是:是否有简便方法递归应用.passthrough(),或修改Zod的默认行为?

我尝试编写如下工具文件,但语法存在问题:

import { z, ZodType } from 'zod';

type ZObject = typeof z.object;

declare module 'zod' {
    export interface ZodType {
      objectPassthrougth : ZObject;
    }
}

z.ZodObject.prototype.objectPassthrougth = function(shape, params){
  return z.object(shape,params).passthrough();
}

const schema =   
  z.objectPassthrougth({
    some : z.string()
  })

解决方案

方案1:自定义递归工具函数

编写一个递归函数,遍历整个Schema结构,自动为所有嵌套的ZodObject添加.passthrough():

import { z, ZodObject, ZodTypeAny } from 'zod';

function recursivePassthrough<T extends ZodTypeAny>(schema: T): T {
  if (schema instanceof ZodObject) {
    // 递归处理每个子schema
    const newShape = Object.fromEntries(
      Object.entries(schema.shape).map(([key, subSchema]) => [
        key,
        recursivePassthrough(subSchema)
      ])
    );
    return z.object(newShape).passthrough() as unknown as T;
  }
  // 非对象类型直接返回原schema
  return schema;
}

// 使用示例
const dataSchema = recursivePassthrough(z.object({
  coords: z.object({
    x: z.number(),
    y: z.number()
  }),
  color: z.object({
    r: z.number(),
    g: z.number(),
    b: z.number()
  })
}));

// 验证后会保留所有未定义的属性
const result = dataSchema.parse(someObjectFromAnApi);

方案2:扩展Zod,创建默认带Passthrough的Object方法

如果你想修改Zod的默认行为,让自定义的对象方法自动递归应用.passthrough(),可以修正你之前的扩展代码:

import { z, ZodObject, ZodRawShape } from 'zod';

// 扩展Zod的类型声明
declare module 'zod' {
  interface ZodType {
    objectPassthrough: <Shape extends ZodRawShape>(
      shape: Shape,
      params?: Parameters<typeof z.object>[1]
    ) => ZodObject<Shape>;
  }
}

// 实现自定义方法
z.objectPassthrough = function<Shape extends ZodRawShape>(
  shape: Shape,
  params?: Parameters<typeof z.object>[1]
) {
  // 递归处理嵌套对象
  const processedShape = Object.fromEntries(
    Object.entries(shape).map(([key, subSchema]) => {
      if (subSchema instanceof ZodObject) {
        return [key, z.objectPassthrough(subSchema.shape)];
      }
      return [key, subSchema];
    })
  ) as Shape;

  return z.object(processedShape, params).passthrough();
};

// 使用示例
const dataSchema = z.objectPassthrough({
  coords: z.object({
    x: z.number(),
    y: z.number()
  }),
  color: z.object({
    r: z.number(),
    g: z.number(),
    b: z.number()
  })
});

注意事项

  • 上述代码基于Zod v3版本编写,若使用v4版本需根据官方API调整细节;
  • 若API返回数据存在循环引用,需在递归函数中添加缓存逻辑,避免栈溢出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 18:43:11