如何用Zod动态生成Schema?React Hook Form场景需求
动态生成Zod Schema实现方案
要实现从字段数组动态生成Zod Schema,同时保留TypeScript类型推断以适配react-hook-form的useForm,可以按以下步骤操作:
1. 定义字段类型与生成函数
首先明确表单字段的结构,再编写生成函数将字段数组转换为Zod Object Schema:
import { z } from "zod"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; // 定义表单字段的通用类型 type FormField<T extends z.ZodTypeAny> = { name: string; fieldType: T; // 可扩展其他属性:label、className、placeholder等 }; // 动态生成Zod Schema的核心函数 const generateSchemaFromFields = <T extends readonly FormField<any>[]>(fields: T) => { // 遍历字段数组,组装成Zod对象所需的键值对 const schemaEntries = fields.map(field => [field.name, field.fieldType] as const); // 转换为Zod Object return z.object(Object.fromEntries(schemaEntries)) as z.ZodObject<{ // 通过泛型推断每个字段的类型 [K in T[number]["name"]]: Extract<T[number], { name: K }>["fieldType"]; }>; };
2. 使用示例
定义静态字段数组
注意要添加as const,让TypeScript捕获每个字段的具体字面量类型:
const formFields = [ { name: "foo", fieldType: z.string().min(1, "Foo不能为空"), label: "Foo输入框", className: "form-control" }, { name: "bar", fieldType: z.number().int().positive("Bar必须是正整数"), label: "Bar输入框", className: "form-control" } ] as const;
生成Schema并推断类型
// 生成Zod Schema const formSchema = generateSchemaFromFields(formFields); // 推断表单字段的TypeScript类型 type FormValues = z.infer<typeof formSchema>; // 类型结果:{ foo: string; bar: number; }
配合react-hook-form使用
const FormComponent = () => { const { register, handleSubmit, formState: { errors } } = useForm<FormValues>({ resolver: zodResolver(formSchema), defaultValues: { foo: "", bar: 0 } }); const onSubmit = (data: FormValues) => { console.log("表单数据:", data); }; return ( <form onSubmit={handleSubmit(onSubmit)}> {formFields.map(field => ( <div key={field.name} className="form-group"> <label>{field.label}</label> <input {...register(field.name as keyof FormValues)} className={`${field.className} ${errors[field.name] ? "is-invalid" : ""}`} type={field.fieldType instanceof z.ZodNumber ? "number" : "text"} /> {errors[field.name] && <span className="invalid-feedback">{errors[field.name]?.message}</span>} </div> ))} <button type="submit">提交</button> </form> ); };
关键注意事项
as const的必要性:确保TypeScript能识别字段数组中每个name和fieldType的具体类型,而非宽泛的string或ZodTypeAny,否则类型推断会失效。- 字段唯一性:需保证字段数组中
name值唯一,否则生成Schema时后面的字段会覆盖前面的。 - 类型断言的作用:函数中的类型断言是为了让Zod正确关联字段名与对应的校验规则,确保
z.infer能得到准确的表单值类型。
内容的提问来源于stack exchange,提问作者Florian
相关产品推荐
相关产品推荐

