React Hook Form在TypeScript中出现FieldError无required属性错误
React Hook Form迁移到TypeScript后类型错误问题解析
问题描述
在React.js页面使用React Hook Form做表单验证一切正常,但迁移到TypeScript页面后出现以下错误:
Property 'required' does not exist on type 'FieldError | Merge<FieldError, FieldErrorsImpl> | MultipleFieldErrors'.
Property 'required' does not exist on type 'FieldError'.
相关代码如下:
import { useForm } from "react-hook-form"; import { GiInfo } from "react-icons/gi"; export default function TestPage() { const {register,getValues,formState: { errors },handleSubmit} = useForm({criteriaMode: "all",mode: "onChange"}); const onSubmit = (data) => console.log(data); return ( <div className='container mt-5 Validation_From'> <div className='bg-light rounded border border-2 p-3 mt-5'> <form onSubmit={handleSubmit(onSubmit)}> <div className='row align-items-center'> <div className="col-md-3 position-relative mb-2"> <label className="form-label">Name</label> <input className="form-control" type="text" id="name" autoComplete="off" {...register("name", { required: true, minLength: 2, })} /> {errors?.name?.types?.required && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} {errors?.name?.types?.minLength && <div className='c_validation'><GiInfo/>Name minLength 2 </div>} </div> <div className="col-md-3 position-relative mb-2"> <label className="form-label">Email</label> <input className="form-control" type="text" id="email" autoComplete="off" {...register("email", { required: true, pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i })} /> {errors?.email?.types?.required && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} {errors?.email?.types?.pattern && <div className='c_validation'><GiInfo/> Invalid email address </div>} </div> <div className="col-md-3 position-relative mb-2"> <label className="form-label">Password</label> <input className="form-control" type="password" autoComplete="off" id="password" {...register("password", { required: true, minLength: 10, pattern: /\d+/gi })} /> {errors?.password?.types?.required && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} {errors?.password?.types?.minLength && <div className='c_validation'><GiInfo/> Password minLength 10 </div>} {errors?.password?.types?.pattern && <div className='c_validation'><GiInfo/> Password number only </div>} </div> <div className="col-md-3 position-relative mb-2"> <label className="form-label">Confirm Password</label> <input className="form-control" type="password" autoComplete="off" id="conpassword" {...register("conpassword", { required: true, validate: (value) => value === getValues("password") })} /> {errors?.conpassword?.types?.required && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} {errors.conpassword && errors.conpassword.type === "validate" && <div className='c_validation'><GiInfo/>Passwords do not match</div>} </div> <div className="col-md-12 position-relative mb-2 text-end"> <button className="btn btn-primary rounded mt-4">Submit</button> </div> </div> </form> </div> </div> ) }
问题原因
- TypeScript强类型约束:React Hook Form在TypeScript中对错误对象的类型定义与JavaScript环境不同。
FieldError类型并没有types属性,你之前在JS中使用的errors?.name?.types?.required写法属于动态类型的宽松用法,在TS的强类型检查下会报错。 - 缺少表单数据类型定义:你没有给
useForm指定表单数据的泛型类型,导致TypeScript无法正确推断errors对象的结构,无法识别required这类验证规则对应的错误标识。
修复方案
1. 定义表单数据类型接口
先声明表单字段对应的TypeScript接口,让useForm能准确推断错误结构:
interface FormData { name: string; email: string; password: string; conpassword: string; }
2. 给useForm指定泛型
初始化useForm时传入上述接口,明确表单数据的类型:
const { register, getValues, formState: { errors }, handleSubmit } = useForm<FormData>({ criteriaMode: "all", mode: "onChange" });
3. 修改错误判断逻辑
React Hook Form在TS中推荐通过error.type字段判断错误类型,替代原来的types属性写法:
- 原写法:
errors?.name?.types?.required - 新写法:
errors?.name?.type === 'required'
同理,所有错误判断都需要调整为这种形式,比如:
// 验证必填项 {errors?.name?.type === 'required' && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} // 验证最小长度 {errors?.name?.type === 'minLength' && <div className='c_validation'><GiInfo/>Name minLength 2 </div>} // 验证邮箱格式 {errors?.email?.type === 'pattern' && <div className='c_validation'><GiInfo/> Invalid email address </div>}
完整修复后的代码
import { useForm } from "react-hook-form"; import { GiInfo } from "react-icons/gi"; interface FormData { name: string; email: string; password: string; conpassword: string; } export default function TestPage() { const { register, getValues, formState: { errors }, handleSubmit } = useForm<FormData>({ criteriaMode: "all", mode: "onChange" }); const onSubmit = (data: FormData) => console.log(data); return ( <div className='container mt-5 Validation_From'> <div className='bg-light rounded border border-2 p-3 mt-5'> <form onSubmit={handleSubmit(onSubmit)}> <div className='row align-items-center'> <div className="col-md-3 position-relative mb-2"> <label className="form-label">Name</label> <input className="form-control" type="text" id="name" autoComplete="off" {...register("name", { required: true, minLength: 2, })} /> {errors?.name?.type === 'required' && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} {errors?.name?.type === 'minLength' && <div className='c_validation'><GiInfo/>Name minLength 2 </div>} </div> <div className="col-md-3 position-relative mb-2"> <label className="form-label">Email</label> <input className="form-control" type="text" id="email" autoComplete="off" {...register("email", { required: true, pattern: /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i })} /> {errors?.email?.type === 'required' && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} {errors?.email?.type === 'pattern' && <div className='c_validation'><GiInfo/> Invalid email address </div>} </div> <div className="col-md-3 position-relative mb-2"> <label className="form-label">Password</label> <input className="form-control" type="password" autoComplete="off" id="password" {...register("password", { required: true, minLength: 10, pattern: /\d+/gi })} /> {errors?.password?.type === 'required' && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} {errors?.password?.type === 'minLength' && <div className='c_validation'><GiInfo/> Password minLength 10 </div>} {errors?.password?.type === 'pattern' && <div className='c_validation'><GiInfo/> Password must contain a number </div>} </div> <div className="col-md-3 position-relative mb-2"> <label className="form-label">Confirm Password</label> <input className="form-control" type="password" autoComplete="off" id="conpassword" {...register("conpassword", { required: true, validate: (value) => value === getValues("password") })} /> {errors?.conpassword?.type === 'required' && <div className='c_validation'><GiInfo/> Please fill out this field. </div>} {errors?.conpassword?.type === 'validate' && <div className='c_validation'><GiInfo/>Passwords do not match</div>} </div> <div className="col-md-12 position-relative mb-2 text-end"> <button className="btn btn-primary rounded mt-4">Submit</button> </div> </div> </form> </div> </div> ) }
内容的提问来源于stack exchange,提问作者Firoz Khan
相关产品推荐
相关产品推荐

