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

如何使用Zod按类型从对象Schema中提取生成新Schema?

问题

假设有如下示例Zod Schema:

const Z_GlobalSettings = z.object({
  crust: z.enum(["thin", "thick"]).default("thin"),
  toppings: z.array(z.string()).min(1).max(5).default(["cheese"]),
  sauce: z.enum(["marinara", "bbq", "ranch"]).default("marinara"),
  delivery_time: z.string().optional(),
  order_notes: z.string().max(50).optional(),
  contact_email: z.string().email().optional(),
  setting_1: z.boolean(),
  setting_2: z.boolean().optional(),
});

type GlobalSettings = z.infer<typeof Z_GlobalSettings>;

如何通过编程方式提取布尔类型或任意指定类型的字段,生成新的Zod Schema?比如提取布尔类型字段后,期望得到的新Schema为:

const Z_OnlyBoolean = z.object({
  setting_1: z.boolean(),
  setting_2: z.boolean().optional(),
});

最初我通过TypeScript类型推导实现了需求:

type BooleanSettings = {
  [K in keyof GlobalSettings as GlobalSettings[K] extends boolean | undefined
    ? K
    : never]?: boolean;
};

但这种方式存在缺陷:无法区分z.string()和z.enum(["example1", "example2"]),因为二者推导后的TypeScript类型均为string。而Zod本身是可以区分这两种类型的。

目前只能通过Zod文档中的.pick()方法手动选择字段,但后续新增字段时需要同步维护过滤后的Schema,且每种类型都要手动创建新Schema,可用性很差。请问该如何实现按Zod类型自动提取并生成新Schema?

解决方案

要实现按Zod自身的类型自动提取字段,核心是利用Zod Schema实例的内部属性判断字段类型,而非依赖TypeScript推导后的类型。下面提供通用的实现方案:

1. 编写通用类型提取工具函数

这个函数接收原始Schema和类型判断逻辑,返回过滤后的新Schema:

import { z, ZodType, ZodObject } from "zod";

function pickByZodType<T extends ZodObject<any>>(
  originalSchema: T,
  isTargetType: (schema: ZodType) => boolean
) {
  const filteredFields: Record<string, ZodType> = {};
  
  // 遍历原始Schema的所有字段
  for (const [key, schema] of Object.entries(originalSchema.shape)) {
    // 处理可选字段:取出ZodOptional包裹的原始类型
    const actualSchema = schema instanceof z.ZodOptional ? schema.unwrap() : schema;
    
    // 判断字段是否符合目标类型
    if (isTargetType(actualSchema)) {
      filteredFields[key] = schema; // 保留原字段的所有约束(optional、default等)
    }
  }
  
  return z.object(filteredFields);
}

2. 提取布尔类型字段的示例调用

用上述工具函数提取布尔类型字段:

// 定义布尔类型的判断逻辑
const isBooleanSchema = (schema: ZodType) => schema instanceof z.ZodBoolean;

// 生成仅包含布尔类型字段的Schema
const Z_OnlyBoolean = pickByZodType(Z_GlobalSettings, isBooleanSchema);

生成的Z_OnlyBoolean会完整保留原字段的optional约束,完全符合预期。

3. 区分普通字符串与枚举类型的示例

如果要提取普通z.string()字段、排除z.enum()类型,可以这样写:

const isPlainStringSchema = (schema: ZodType) => {
  return schema instanceof z.ZodString && !(schema instanceof z.ZodEnum);
};

// 提取仅包含普通字符串类型的字段
const Z_OnlyPlainString = pickByZodType(Z_GlobalSettings, isPlainStringSchema);

这样就能精准区分Zod的普通字符串和枚举类型,不会出现混淆。

核心优势

  • 直接基于Zod实例的类型判断,完全规避TypeScript类型推导的局限性
  • 保留原字段的所有约束(包括optional、default、min/max等),无需重复定义
  • 新增字段时,只要类型符合判断条件就会自动纳入新Schema,无需手动维护

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 17:02:41