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

如何通过Zod自动检测表单字段是否可选,为必填项添加*标记?

从Zod中提取字段可选性,自动为必填项添加*标记

是的,你可以直接从Zod Schema中提取字段的可选性信息,从而自动为必填项的标签添加*标记。以下是具体实现步骤:

1. 编写字段必填性检测函数

首先,我们需要一个辅助函数,根据字段路径(支持嵌套字段)遍历Zod Schema,判断该字段是否为必填项:

import { z } from 'zod';

const isFieldRequired = (schema: z.ZodObject<any>, fieldPath: string): boolean => {
  const segments = fieldPath.split('.');
  let currentSchema = schema;

  for (const segment of segments) {
    if (!currentSchema.shape[segment]) {
      throw new Error(`无效的字段路径: ${fieldPath}`);
    }
    currentSchema = currentSchema.shape[segment];
  }

  // 若字段不是可选的,则为必填项
  return !currentSchema.isOptional;
};

2. 修改自定义表单字段组件

在你的CustomFormInput组件中,导入Zod Schema并使用上述函数判断字段是否必填,然后在标签中添加*:

import { FormSchema } from './path-to-your-schema'; // 替换为你的Schema路径
// 其他必要导入(如FormField、FormItem等)

interface CustomFormInputProps {
  isLoading: boolean;
  control: Control<z.infer<typeof FormSchema>>;
  name: FieldPath<z.infer<typeof FormSchema>>;
  label: string;
  placeholder: string;
}

const CustomFormInput = ({ isLoading, control, name, label, placeholder }: CustomFormInputProps) => {
  const isRequired = isFieldRequired(FormSchema, name);

  return (
    <FormField
      disabled={isLoading}
      control={control}
      name={name}
      render={(field) => (
        <FormItem>
          <FormLabel>
            {label} 
            {isRequired && <span className="text-red-500 ml-1">*</span>}
          </FormLabel>
          <FormControl>
            <Input
              type={name === 'password' ? 'password' : 'text'}
              placeholder={placeholder}
              {...field}
            />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
  );
};

export default CustomFormInput;

3. 让组件支持多Schema复用(可选)

如果你希望组件能适配不同的Zod Schema,可以将Schema作为props传入,增强组件的复用性:

import { z } from 'zod';
import { Control, FieldPath } from 'react-hook-form';
// 其他必要导入

interface CustomFormInputProps<T extends z.ZodObject<any>> {
  isLoading: boolean;
  control: Control<z.infer<T>>;
  name: FieldPath<z.infer<T>>;
  label: string;
  placeholder: string;
  schema: T;
}

const isFieldRequired = <T extends z.ZodObject<any>>(schema: T, fieldPath: string): boolean => {
  const segments = fieldPath.split('.');
  let currentSchema: any = schema;

  for (const segment of segments) {
    if (!currentSchema.shape[segment]) {
      throw new Error(`无效的字段路径: ${fieldPath}`);
    }
    currentSchema = currentSchema.shape[segment];
  }

  return !currentSchema.isOptional;
};

const CustomFormInput = <T extends z.ZodObject<any>>({ isLoading, control, name, label, placeholder, schema }: CustomFormInputProps<T>) => {
  const isRequired = isFieldRequired(schema, name);

  return (
    <FormField
      disabled={isLoading}
      control={control}
      name={name}
      render={(field) => (
        <FormItem>
          <FormLabel>
            {label} 
            {isRequired && <span className="text-red-500 ml-1">*</span>}
          </FormLabel>
          <FormControl>
            <Input
              type={name === 'password' ? 'password' : 'text'}
              placeholder={placeholder}
              {...field}
            />
          </FormControl>
          <FormMessage />
        </FormItem>
      )}
    />
  );
};

export default CustomFormInput;

使用时传入对应的Schema:

<CustomFormInput
  isLoading={false}
  control={control}
  name="email"
  label="Email"
  placeholder="Enter your email"
  schema={FormSchema}
/>

注意事项

  • 若字段是nullable(如z.string().nullable()),isOptional会返回false,如果需要将nullable字段也视为非必填,可以修改判断逻辑为return !currentSchema.isOptional && !currentSchema.isNullable;
  • 对于包含undefined的联合类型(如z.string().or(z.undefined())),isOptional会返回true,等同于.optional()的效果

内容的提问来源于stack exchange,提问作者Rendo Hugh Mann Sama

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 14:54:51