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

如何在Zod中根据字段值条件扩展Schema并确保类型推断?

解决方案

要实现根据字段a的值动态包含不同Schema的需求,最适合用Zod的**可辨识联合(Discriminated Union)**特性,它能完美覆盖类型推断、分支验证和错误提示的要求。

步骤1:修正原Schema的语法问题

原代码中schemaB和schemaC的字段末尾缺少逗号,先补全并拆分基础Schema:

import z from "zod";
import validator from "validator"; // 假设已导入验证工具库

// 提取schemaA的基础字段,作为两个分支的公共部分
const schemaABase = z.object({
  a: z.enum(['b', 'c']),
  b: z.number().optional(),
  c: z.boolean().transform((val) => +val) // boolean转数字(0|1)
});

const schemaB = z.object({
  d: z.string().refine(validator.isIpAddress),
  e: z.number(),
  password: z.string(),
  passwordConfirm: z.string(),
}).refine((data) => data.password === data.passwordConfirm, { 
  path: ['passwordConfirm'], // 将错误指向确认密码字段更直观
  message: '两次输入的密码不匹配!' 
});

const schemaC = z.object({
  d: z.string().refine(validator.isMacAddress),
  f: z.boolean().transform((val) => +val) // boolean转数字(0|1)
});

步骤2:构建分支Schema并组合成可辨识联合

基于a字段的枚举值,分别构建两个分支的Schema,再用z.discriminatedUnion组合:

// 分支1:当a为"b"时,合并基础字段与schemaB的所有字段
const schemaAB = schemaABase.extend({
  a: z.literal('b')
}).merge(schemaB);

// 分支2:当a为"c"时,合并基础字段与schemaC的所有字段
const schemaAC = schemaABase.extend({
  a: z.literal('c')
}).merge(schemaC);

// 最终的可辨识联合Schema
const finalSchema = z.discriminatedUnion('a', [schemaAB, schemaAC]);

满足需求的核心原因

  • 动态字段匹配:通过merge方法将基础字段与对应分支Schema合并,结合literal固定a的值,确保a为"b"时仅加载schemaB字段,为"c"时仅加载schemaC字段。
  • 准确的类型推断:使用zod.infer<typeof finalSchema>会自动生成区分场景的联合类型,完全覆盖两种情况下的字段类型:
    type FinalType = z.infer<typeof finalSchema>;
    // 等价于:
    // type FinalType = 
    //   ({ a: "b"; b?: number; c: 0 | 1 } & { d: string; e: number; password: string; passwordConfirm: string })
    //   | ({ a: "c"; b?: number; c: 0 | 1 } & { d: string; f: 0 | 1 })
    
  • 完整的错误提示:Zod的可辨识联合会验证对应分支的所有规则,无论是IP/MAC格式错误、密码不匹配还是必填字段缺失,都会返回包含具体字段路径和提示信息的错误,不会仅返回通用提示。

验证示例

// 验证失败案例:a为"b"但密码不匹配
const result = finalSchema.safeParse({
  a: "b",
  c: true,
  d: "192.168.1.1",
  e: 123,
  password: "123456",
  passwordConfirm: "654321"
});

if (!result.success) {
  console.log(result.error.issues);
  // 输出错误包含:
  // [{ code: 'custom', message: '两次输入的密码不匹配!', path: ['passwordConfirm'], ... }]
}

内容的提问来源于stack exchange,提问作者Islam Y-

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 05:34:56