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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 16:05:22