React Hook Form结合Yup Schema使用时可选字段引发的类型错误问题
我之前也碰到过一模一样的类型问题,本质是Yup的类型推断逻辑和React Hook Form的类型期望没对齐导致的,咱们一步步拆解问题和解决办法:
问题根源
你通过yup.InferType<typeof yupSchema>得到的YupFormData里,surname和age的类型是surname: string | undefined、age: number | undefined——这代表这些字段是必选属性,但值允许为undefined。
但React Hook Form期望的可选字段是可选属性(也就是surname?: string | undefined),这两种类型在TypeScript里是完全不同的,所以才会出现 resolver 和 control 的类型不匹配报错。
解决方案
有几种办法可以解决这个类型对齐的问题,选你觉得顺手的就行:
方案1:手动定义FormData类型(最直观)
直接绕过Yup的自动推断,手动写出符合React Hook Form期望的类型:
type FormData = { name: string; // 必选字段 surname?: string; // 可选字段,标记为? age?: number; // 可选字段,标记为? };
然后在useForm里用这个类型代替YupFormData:
const { control, handleSubmit, formState: { errors } } = useForm<FormData>({ resolver: yupResolver(yupSchema), mode: 'onBlur', });
这个方法的好处是类型完全由你掌控,不会受Yup推断逻辑的影响。
方案2:基于Yup推断的类型修改(更贴合Schema)
如果你不想手动写类型,可以基于Yup的推断结果,把可选字段转成可选属性:
// 先保留Yup的自动推断结果 type YupInferred = yup.InferType<typeof yupSchema>; // 把surname和age转为可选属性 type FormData = Omit<YupInferred, 'surname' | 'age'> & Partial<Pick<YupInferred, 'surname' | 'age'>>;
这样既保留了Yup Schema的类型信息,又满足了React Hook Form的类型要求。
方案3:检查版本兼容性
有时候版本不兼容也会触发这类类型错误,建议确保react-hook-form和@hookform/resolvers的版本是匹配的(比如都升级到最新的稳定版)。比如当前react-hook-form的稳定版是v7.x,对应的@hookform/resolvers也要用v7.x的版本。
解决InputController的类型问题
当你用上面调整后的FormData类型后,InputController的泛型参数也会自动对齐,control的类型不匹配问题就会消失:
// 调整InputController的Props定义(保持原有逻辑即可) type InputControllerProps<T extends FieldValues> = { name: Path<T>; control: Control<T>; error?: string; label: string; // 你的其他属性... }; // 使用组件时,类型会自动推导 <InputController name="name" control={control} error={errors.name?.message} label="Name" /> <InputController name="surname" control={control} error={errors.surname?.message} label="Surname" /> <InputNumberController name="age" control={control} error={errors.age?.message} label="Age" />
这样处理后,不管是resolver的类型错误,还是control的类型不匹配问题,应该都能顺利解决。
内容来源于stack exchange

