如何在Zod中实现可选字段存在时禁止undefined值?
解决Zod中可选字段禁止传入undefined的问题
需求回顾
你需要实现:对象的可选字段要么完全缺失,要么传入有效字符串,禁止出现“字段存在但值为undefined”的情况,即:
- ✅
{ other: "other" }(通过) - ❌
{ optional: undefined, other: "other" }(失败) - ✅
{ optional: "str", other: "other" }(通过)
你的现有方案用z.object().or().or()组合,虽然可行,但可选字段增多时会产生指数级的组合,维护成本极高。下面是更优雅的实现方式:
方案1:对象层面用superRefine校验(推荐)
利用Zod的superRefine可以对整个对象进行自定义校验,直接检查指定字段是否存在且值为undefined:
import { z } from "zod"; const objectSchema = z.object({ optional: z.string().optional(), other: z.string() }).strict().superRefine((obj, ctx) => { // 检查单个可选字段 if ("optional" in obj && obj.optional === undefined) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: "optional字段若存在则不能为undefined", path: ["optional"] // 指定报错字段路径 }); } // 多个可选字段时,直接追加检查逻辑即可 // if ("anotherOptional" in obj && obj.anotherOptional === undefined) { // ctx.addIssue({ ... }); // } }); // 测试验证 objectSchema.parse({ other: "other" }); // ✅ 通过 objectSchema.parse({ optional: undefined, other: "other" }); // ❌ 抛出错误 objectSchema.parse({ optional: "str", other: "other" }); // ✅ 通过
这个方案的优势是:
- 可选字段增多时,只需在
superRefine里追加检查逻辑,线性扩展,维护成本低 - 报错信息清晰,能准确定位到问题字段
方案2:封装自定义可选字段Schema(复用性强)
如果需要在多个Schema中复用“可选但不能为undefined”的规则,可以封装一个自定义Schema函数:
import { z } from "zod"; /** * 创建一个可选字段Schema:字段可缺失,但存在时不能为undefined,必须符合传入的schema规则 */ const optionalButNotUndefined = <T extends z.ZodTypeAny>(schema: T, message?: string) => z.union([schema, z.undefined()]) .refine(val => val !== undefined, { message: message ?? "该字段若存在则不能为undefined" }) .optional(); // 使用示例 const objectSchema = z.object({ optional: optionalButNotUndefined(z.string()), anotherOptional: optionalButNotUndefined(z.number(), "数字可选字段不能为undefined"), other: z.string() }).strict(); // 测试验证 objectSchema.parse({ other: "other" }); // ✅ 通过 objectSchema.parse({ optional: undefined, other: "other" }); // ❌ 抛出错误 objectSchema.parse({ anotherOptional: undefined, other: "other" }); // ❌ 抛出错误 objectSchema.parse({ optional: "str", anotherOptional: 123, other: "other" }); // ✅ 通过
这个方案适合需要在多个地方复用相同规则的场景,代码更简洁。
注意点
单独对字段用z.string().optional().refine(val => val !== undefined)不可行:当字段缺失时,val是undefined,此时refine会触发校验失败,不符合“字段可缺失”的需求。而上面的方案要么在对象层面检查字段是否存在,要么通过union+optional的组合,确保字段缺失时不触发校验。
内容的提问来源于stack exchange,提问作者Yunhai
相关产品推荐
相关产品推荐

