如何为React Hook Form的自定义Input组件实现灵活TypeScript类型
解决方案:让Input组件成为泛型组件
要解决这个类型不兼容问题,最直接的方式是把Input组件定义为泛型组件,让它适配任意表单值类型,而非提前固定为联合类型。这样既能兼容登录、注册表单,还能复用在其他表单场景。
具体实现代码
1. 定义泛型InputProps和组件
import { Path, UseFormRegister } from "react-hook-form"; // 泛型Props,T代表当前表单的values类型 type InputProps<T> = { hookValue: Path<T>; register: UseFormRegister<T>; id: string; label: string; // 其他你需要的额外props }; // 泛型Input组件 const Input = <T extends object>({ hookValue, register, id, label, ...rest }: InputProps<T>) => { return ( <div> <label htmlFor={id}>{label}</label> <input {...register(hookValue)} id={id} {...rest} /> </div> ); };
2. 在登录/注册表单中使用
登录表单(适配ISignIn类型)
import { useForm } from "react-hook-form"; interface ISignIn { email: string; password: string; } const SignInForm = () => { const { register } = useForm<ISignIn>(); return ( <form> <Input<ISignIn> register={register} hookValue="email" label="Email" type="email" /> <Input<ISignIn> register={register} hookValue="password" label="Password" type="password" /> <button type="submit">登录</button> </form> ); };
注册表单(适配ISignUp类型)
interface ISignUp extends ISignIn { confirmPassword: string; } const SignUpForm = () => { const { register } = useForm<ISignUp>(); return ( <form> <Input<ISignUp> register={register} hookValue="email" label="Email" type="email" /> <Input<ISignUp> register={register} hookValue="password" label="Password" type="password" /> <Input<ISignUp> register={register} hookValue="confirmPassword" label="Confirm Password" type="password" /> <button type="submit">注册</button> </form> ); };
为什么之前的联合类型方案会报错?
TypeScript中,UseFormRegister的类型是逆变的。当你期望UseFormRegister<ISignUp | ISignIn>时,传入UseFormRegister<ISignUp>是不兼容的——因为ISignUp是更具体的类型,而联合类型要求的是能处理两种类型的register函数,反过来才成立(UseFormRegister<ISignUp | ISignIn>可以赋值给UseFormRegister<ISignUp>)。
泛型方案则让组件针对每个具体的表单类型生成对应的类型定义,完美规避了逆变带来的类型不兼容问题。
内容的提问来源于stack exchange,提问作者Le Moi
相关产品推荐
相关产品推荐

