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

如何根据父对象出生日期验证Zod中tutor对象的必填性

Zod条件验证:根据年龄动态要求监护人字段

当然可以实现这种动态验证逻辑,下面提供两种简洁且符合Zod设计思路的方案:

方案1:使用Union类型拆分两种场景(推荐,类型更安全)

这种方式将用户分为「未成年」和「成年」两个独立的Schema,通过Union类型合并,Zod会自动匹配对应的验证规则,同时TypeScript能根据年龄推断出tutor字段的可选性。

首先定义计算年龄的辅助函数:

function calculateAge(birthdate: Date): number {
  const today = new Date();
  const age = today.getFullYear() - birthdate.getFullYear();
  const monthDiff = today.getMonth() - birthdate.getMonth();
  // 处理生日未过的情况,年龄减1
  if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthdate.getDate())) {
    return age - 1;
  }
  return age;
}

然后拆分并合并Schema:

// 基础公共字段Schema
const baseMemberSchema = z.object({
  name: z.string().min(3),
  lastname: z.string().min(3),
  nationality: zCountries,
  birthdate: z.date(),
  email: z.string().email(),
});

// 未成年用户Schema:强制要求监护人字段
const minorMemberSchema = baseMemberSchema.extend({
  tutor: z.object({
    name: z.string(),
    lastname: z.string(),
    documentType: zDocumentationType,
    documentValue: z.string(),
    email: z.string().email(),
    phone: z.string().regex(/^\d{9}$/)
  })
}).refine(data => calculateAge(data.birthdate) < 18, {
  message: "未成年用户必须提供完整的监护人信息",
  path: ["birthdate"]
});

// 成年用户Schema:监护人字段可选(若提供则需符合格式)
const adultMemberSchema = baseMemberSchema.extend({
  tutor: z.object({
    name: z.string(),
    lastname: z.string(),
    documentType: zDocumentationType,
    documentValue: z.string(),
    email: z.string().email(),
    phone: z.string().regex(/^\d{9}$/)
  }).optional()
}).refine(data => calculateAge(data.birthdate) >= 18, {
  message: "成年用户无需提供监护人信息(若提供则需符合格式要求)",
  path: ["birthdate"]
});

// 合并为最终Schema
export const memberSchema = z.union([minorMemberSchema, adultMemberSchema]);

方案2:使用SuperRefine集中处理逻辑

如果不想拆分Schema,可以用superRefine在同一个Schema中添加自定义验证逻辑,同时保留Zod对tutor对象本身的结构验证:

function calculateAge(birthdate: Date): number {
  const today = new Date();
  const age = today.getFullYear() - birthdate.getFullYear();
  const monthDiff = today.getMonth() - birthdate.getMonth();
  if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthdate.getDate())) {
    return age - 1;
  }
  return age;
}

export const memberSchema = z
  .object({
    name: z.string().min(3),
    lastname: z.string().min(3),
    nationality: zCountries,
    birthdate: z.date(),
    email: z.string().email(),
    tutor: z
      .object({
        name: z.string(),
        lastname: z.string(),
        documentType: zDocumentationType,
        documentValue: z.string(),
        email: z.string().email(),
        phone: z.string().regex(/^\d{9}$/)
      })
      .optional() // 先标记为可选
  })
  .superRefine((data, ctx) => {
    const age = calculateAge(data.birthdate);
    // 未满18岁时,检查监护人是否存在且符合要求
    if (age < 18) {
      if (!data.tutor) {
        ctx.addIssue({
          code: z.ZodIssueCode.custom,
          message: "未成年用户必须填写监护人信息",
          path: ["tutor"]
        });
      }
      // 注:tutor对象的字段验证已由Zod自动完成,无需重复检查
    }
    // 满18岁时,tutor可选,若提供则Zod会自动验证其结构
  });

验证效果

  • 当birthdate对应的年龄未满18岁时:必须提供完整的tutor对象,且所有字段需符合验证规则,否则报错。
  • 当birthdate对应的年龄已满18岁时:tutor字段可以为undefined,若提供则必须符合结构验证,否则报错。

内容的提问来源于stack exchange,提问作者Elena Martín Castillo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:06:29