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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 20:55:23