
本文介绍使用 Zod 的 Discriminated Union(判别式联合)实现条件化 Schema:当某个布尔字段(如 showShippingAddress)为 true 时,强制校验关联字段;为 false 时则忽略这些字段,避免冗余 .superRefine() 手动校验。
本文介绍使用 zod 的 discriminated union(判别式联合)实现条件化 schema:当某个布尔字段(如 `showshippingaddress`)为 true 时,强制校验关联字段;为 false 时则忽略这些字段,避免冗余 `.superrefine()` 手动校验。
在表单验证中,常需根据用户选择(如是否启用配送地址)动态控制后续字段的必填性与校验规则。Zod 原生不支持“运行时条件 Schema”,但通过 Discriminated Union(判别式联合) 可优雅实现该需求——它基于一个固定字面量字段(discriminator),将整个对象划分为互斥的、结构明确的子 Schema。
核心思路是:将 showShippingAddress 字段定义为 z.literal(true) 或 z.literal(false),而非泛化的 z.boolean(),从而让 Zod 能静态识别并路由到对应分支。
以下是一个完整、可直接使用的示例:
import { z } from 'zod';
const formSchema = z.discriminatedUnion('showShippingAddress', [
// 分支一:不显示配送地址
z.object({
showShippingAddress: z.literal(false),
field1: z.string().nonempty('请输入字段1'),
field2: z.string().nonempty('请输入字段2'),
// field3 和 field4 在此分支中完全不存在(非 optional,而是彻底移除)
// 若需兼容旧数据或 API,可显式设为 optional,但语义上更推荐彻底排除
}),
// 分支二:显示配送地址 → field3、field4 变为必填且带额外校验
z.object({
showShippingAddress: z.literal(true),
field1: z.string().nonempty('请输入字段1'),
field2: z.string().nonempty('请输入字段2'),
field3: z.string().nonempty('配送地址不能为空'),
field4: z.string()
.nonempty('邮编不能为空')
.regex(/^\d{6}$/, '请填写6位中国邮政编码'),
}),
]);
// ✅ 正确解析(showShippingAddress = false)
formSchema.parse({
showShippingAddress: false,
field1: '张三',
field2: '13800138000',
});
// ✅ 正确解析(showShippingAddress = true,且所有字段完整)
formSchema.parse({
showShippingAddress: true,
field1: '李四',
field2: '13900139000',
field3: '北京市朝阳区建国路1号',
field4: '100022',
});
// ❌ 校验失败:showShippingAddress = true 但缺少 field3
// formSchema.parse({ showShippingAddress: true, field1: 'A', field2: 'B' }); // 报错
// ❌ 校验失败:field4 不符合正则
// formSchema.parse({ showShippingAddress: true, field1: 'A', field2: 'B', field3: 'X', field4: '123' }); // 报错
⚠️ 关键注意事项:
- discriminator 字段必须是 literal 类型(如 z.literal(true)),不能用 z.boolean(),否则 Zod 无法静态区分分支;
- 每个分支应完全独立定义字段:不需要的字段不要声明为 .optional(),而应彻底省略——这能提升类型安全性和 IDE 自动补全精度;
- 若后端 API 允许 field3/field4 在 showShippingAddress: false 时存在(如历史数据兼容),才考虑在 false 分支中添加 .optional(),但需权衡语义清晰度;
- 所有公共字段(如 field1, field2)必须在每个分支中重复定义,确保类型一致性;
- 使用 .nonempty() 替代 .nullable().min(1),语义更准确且避免空字符串通过校验。
相比手动 superRefine,判别式联合的优势在于:
✅ 类型推导精准(TypeScript 可精确识别各分支的 shape)
✅ 错误信息更清晰(Zod 直接指出“缺少 required field”而非自定义 message)
✅ 无运行时逻辑膨胀(无需维护大量 if 校验块)
✅ 支持嵌套、组合与复用(可将分支 Schema 提取为常量)
综上,面对“字段依赖布尔开关”的场景,优先选用 z.discriminatedUnion —— 它是 Zod 官方推荐、类型安全、可维护性高的标准解法。










