Zod实现邮箱与用户名输入双重验证的问题求助
解决Zod中邮箱/用户名联合验证失效的问题
问题原因
你当前的Zod Union校验逻辑失效,是因为Zod Union按顺序匹配规则,且普通字符串规则的包容性远强于邮箱规则。只要输入满足非空、长度合规的字符串,就会直接匹配第二个分支(用户名规则),不会触发前面的邮箱格式校验。
解决方案
方法一:使用z.disjunctiveUnion(Zod v3.20+)
disjunctiveUnion会检查所有分支,仅当恰好一个分支验证通过时才判定有效,完美解决Union的顺序匹配问题。同时建议给用户名规则添加额外限制(比如禁止包含@),避免和邮箱混淆。
import { z } from "zod"; const authSchema = z.object({ emailOrUsername: z.disjunctiveUnion([ // 邮箱分支 z.string() .min(1, 'Email is required') .email('Invalid email') .max(100) .trim(), // 用户名分支 z.string() .min(1, 'Username is required') .max(100) .trim() .refine(val => !val.includes('@'), 'Username cannot contain @ symbol'), ]), });
方法二:自定义superRefine校验逻辑
如果需要更灵活的校验逻辑(比如复杂的用户名规则),可以用superRefine手动控制验证流程:
import { z } from "zod"; const authSchema = z.object({ emailOrUsername: z.string() .min(1, 'Input is required') .max(100) .trim() .superRefine((val, ctx) => { // 先验证邮箱格式 const emailCheck = z.string().email().safeParse(val); if (emailCheck.success) return; // 验证用户名规则 const usernameCheck = z.string() .refine(v => !v.includes('@'), 'Username cannot contain @') .safeParse(val); if (!usernameCheck.success) { ctx.addIssue({ code: z.ZodIssueCode.custom, message: usernameCheck.error.issues[0].message, }); } }), });
说明
disjunctiveUnion更简洁,适合常规场景;superRefine适合需要自定义复杂校验逻辑的场景;- 添加用户名禁止
@的规则是为了避免用户输入类似user@example的字符串被误判为用户名,提升校验准确性。
内容的提问来源于stack exchange,提问作者Girbi
相关产品推荐
相关产品推荐

