如何根据父对象出生日期验证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
相关产品推荐
相关产品推荐

