如何基于isLimitedEdition标记将Zod Schema所有属性设为可选?
问题
我拥有一个用于验证电商应用商品属性的Zod Schema,其中包含isLimitedEdition属性,用于标记商品是否为限量款。当isLimitedEdition设为true时,我希望Schema中的其他所有属性(name、description、price、category、brand、isFeatured)变为可选。
现有Schema结构如下:
const productProperties = z.object({ name: z.string().nonempty(), description: z.string().nonempty(), price: z.number().positive(), category: z.string().nonempty(), brand: z.string().nonempty(), isFeatured: z.boolean().default(false), isLimitedEdition: z.boolean().default(false), });
我曾尝试使用refine方法但未成功,请问如何修改该Schema以实现上述需求?
解决方案
用Zod的discriminatedUnion就能搞定这个需求,它专门处理基于某个区分字段的不同结构验证,比refine直观还容易维护。
具体做法是分两个分支定义Schema,再用联合类型组合:
- 先把通用的商品属性抽出来复用,避免重复写代码
- 定义非限量款的Schema:所有通用字段必填,
isLimitedEdition固定为false - 定义限量款的Schema:所有通用字段改成可选,
isLimitedEdition固定为true - 用
discriminatedUnion把两个分支合并,让Zod根据isLimitedEdition的值自动匹配验证规则
代码实现如下:
import { z } from "zod"; // 抽离通用属性,方便后续修改 const baseProductFields = { name: z.string().nonempty(), description: z.string().nonempty(), price: z.number().positive(), category: z.string().nonempty(), brand: z.string().nonempty(), isFeatured: z.boolean().default(false), }; // 非限量款:所有通用字段必填,isLimitedEdition固定为false const nonLimitedProduct = z.object({ ...baseProductFields, isLimitedEdition: z.literal(false), }); // 限量款:所有通用字段改为可选,isLimitedEdition固定为true const limitedProduct = z.object({ ...Object.fromEntries( Object.entries(baseProductFields).map(([key, schema]) => [key, schema.optional()]) ), isLimitedEdition: z.literal(true), }); // 最终的商品Schema const productProperties = z.discriminatedUnion("isLimitedEdition", [ nonLimitedProduct, limitedProduct, ]);
额外说明
discriminatedUnion会根据isLimitedEdition的值自动切换验证逻辑,TypeScript的类型推断也会更精准,写代码时能拿到正确的字段提示- 通用字段抽离后,后续要修改某个字段的规则(比如price改成非负数),只需要改
baseProductFields一处就行 - 限量款模式下,虽然字段是可选的,但如果传值的话依然会遵守原有的验证规则(比如传name就必须是非空字符串)
- 如果需要保留
isLimitedEdition的默认值,可以在联合Schema外层再包装一层,或者给两个分支的isLimitedEdition对应加上默认值
内容的提问来源于stack exchange,提问作者Mustafa Ahmed Mohammed
相关产品推荐
相关产品推荐

