如何在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-
相关产品推荐
相关产品推荐

