如何将ChoiceOfForm枚举传入ZOD的discriminatedUnion实现动态表单验证?
问题描述
我有一个对应三个单选按钮的ChoiceOfForm枚举(值为1、2、3),分别对应:
- 基础表单(无额外字段)
- 简化表单(仅用户名/密码字段)
- 高级表单(用户名/密码+多字段)
我希望使用Zod库的discriminatedUnion()函数,根据选中的ChoiceOfForm值启用对应的表单验证块。请问是否可以将choiceOfForm变量传入discriminatedUnion(choiceOfForm, ...)来实现该需求?
原代码示例
ValidationSchema = z.object({ AuthenticationBlock: z.object({ ChoiceOfForm: z.enum() DataBlock: z.discriminatedUnion(ChoiceOfForm, [ z.object({ ChoiceOfForm = 1, empty fields}) //corresponds to the basic form z.object({ ChoiceOfForm = 2, just username/pw fields) //corresponds to less basic form z.object({ ChoiceOfForm = 3, user/pw + many fields}) //corresponds to advanced form ]) }) })
对象类型示例
export type AuthenticationFormValues = { AuthenticationBlock?: { choice?: AuthenticationFormChoice | undefined; data: { password?: string; username?: string; formAuthenticationConfig?: { formUrl?: string; javaScriptLoadingDelayInMilliseconds?: number; forceLogin?: boolean; customLoginSequence?: string; authenticationFailed?: FormAuthenticationFailedConfiguration; }; }; }; };
解答
明确结论:不能直接将ChoiceOfForm枚举变量传入discriminatedUnion的第一个参数。Zod的discriminatedUnion要求第一个参数是判别字段的名称字符串,而非枚举变量;同时需要搭配Zod定义的枚举/字面量值来约束分支规则。
以下是正确的实现方式:
1. 先定义Zod版枚举(或字面量)
先把你的单选按钮值转换成Zod可识别的枚举类型,确保验证规则和前端选择值对齐:
// 方式1:用字符串字面量枚举(如果前端传的是字符串类型的1/2/3) const ChoiceOfForm = z.enum(['1', '2', '3']); // 方式2:绑定TypeScript原生枚举(如果用TS枚举定义选项) // enum TSChoiceOfForm { Basic = 1, Simplified = 2, Advanced = 3 } // const ChoiceOfForm = z.nativeEnum(TSChoiceOfForm);
2. 构建分支验证规则
针对三种表单类型,分别定义对应的字段验证规则,每个分支必须包含统一的判别字段:
// 基础表单(无额外字段) const BasicFormSchema = z.object({ choice: z.literal('1'), // 对应ChoiceOfForm枚举的基础选项 }); // 简化表单(仅用户名/密码) const SimplifiedFormSchema = z.object({ choice: z.literal('2'), username: z.string().min(1, '用户名不能为空'), password: z.string().min(6, '密码至少6位'), }); // 高级表单(完整字段) const AdvancedFormSchema = z.object({ choice: z.literal('3'), username: z.string().min(1, '用户名不能为空'), password: z.string().min(6, '密码至少6位'), formAuthenticationConfig: z.object({ formUrl: z.string().url('请输入合法URL'), javaScriptLoadingDelayInMilliseconds: z.number().int().nonnegative(), forceLogin: z.boolean(), customLoginSequence: z.string().optional(), authenticationFailed: z.object({ // 根据AuthenticationFailedConfiguration的结构定义验证规则 }).optional(), }), });
3. 组合成最终验证Schema
用discriminatedUnion把分支规则组合,第一个参数传入判别字段的名称(这里是choice,和你的类型定义对齐):
const ValidationSchema = z.object({ AuthenticationBlock: z.object({ choice: ChoiceOfForm, data: z.discriminatedUnion('choice', [ BasicFormSchema, SimplifiedFormSchema, AdvancedFormSchema, ]), }).optional(), });
关键注意点
discriminatedUnion的第一个参数是字段名称字符串,不是枚举变量;- 每个分支必须包含该判别字段,且值为对应的字面量/枚举值;
- 必须保证Zod Schema的结构和你的TypeScript类型(
AuthenticationFormValues)完全对齐,避免字段名大小写、层级不一致的问题。
内容的提问来源于stack exchange,提问作者Smolcat991
相关产品推荐
相关产品推荐

