React-Hook-Form结合YupResolver与TypeScript时必填字段适配问题
解决React-Hook-Form与自动生成类型的类型不匹配问题
问题背景
我有从OpenAPI自动生成的TypeScript类型,无法手动修改:
export type InventoryItemCreate = { name: string; status_id: number; barcode?: string; //... };
我用Yup定义了React-Hook-Form的验证规则,其中name是必填项:
const schema = Yup.object().shape({ name: Yup.string().required('Name ist ein Pflichtfeld'), barcode: Yup.string() .min(5, 'Der Barcode muss 5-stellig sein') .max(5, 'Der Barcode muss 5-stellig sein') .matches(/^[A-Z][0-9]{4}$/, 'Muss das Format X0000 haben') .optional() .nullable(), rfid_id: Yup.string().optional().nullable(), description: Yup.string().nullable(), wiki_url: Yup.string().optional().nullable(), other_owner: Yup.string().optional().nullable(), model: Yup.string().optional().nullable(), vendor: Yup.string().optional().nullable() });
在组件中使用时:
import { yupResolver } from '@hookform/resolvers/yup'; import * as Yup from 'yup'; export const InventoryItemForm = ({ existingItem }: { existingItem?: InventoryItem }) => { // 省略部分代码 const formContext = useForm<InventoryItemFormSchema>({ resolver: yupResolver(schema), values: existingItem });
出现了类型不匹配的错误:
./components/inventory/InventoryItemForm.tsx:67:5 Type error: Type 'Resolver<{ name?: string; barcode?: string; description?: string; rfid_id?: string; wiki_url?: string; other_owner?: string; model?: string; vendor?: string; }>' is not assignable to type 'Resolver<InventoryItemFormSchema, any>'. Types of parameters 'options' and 'options' are incompatible. Type 'ResolverOptions<InventoryItemFormSchema>' is not assignable to type 'ResolverOptions<{ name?: string; barcode?: string; description?: string; rfid_id?: string; wiki_url?: string; other_owner?: string; model?: string; vendor?: string; }>'. Types of property 'names' are incompatible. Type 'string[]' is not assignable to type 'OptionalKeys<{ name: string; barcode: string; rfid_id: string; description: string; wiki_url: string; other_owner: string; model: string; vendor: string; }>[]'. Type 'string' is not assignable to type 'OptionalKeys<{ name: string; barcode: string; rfid_id: string; description: string; wiki_url: string; other_owner: string; model: string; vendor: string; }>'. 65 | 66 | const formContext = useForm<InventoryItemFormSchema>({ > 67 | resolver: yupResolver(schema), | ^ 68 | values: existingItem 69 | }); 70 | error Command failed with exit code 1.
手动把name改成可选类型能解决错误,但这和Yup中name必填的规则矛盾,需要找到既能保留自动生成类型,又能确保表单返回值中name为非可选的方法。
解决方案
1. 用TypeScript工具类型定义匹配的表单Schema
基于自动生成的InventoryItemCreate,通过工具类型组合出符合验证规则的表单类型:
// 保留name为必填,其他原类型字段和新增字段设为可选 type InventoryItemFormSchema = Pick<InventoryItemCreate, 'name'> & Partial<Omit<InventoryItemCreate, 'name'>> & { rfid_id?: string; description?: string; wiki_url?: string; other_owner?: string; model?: string; vendor?: string; };
这样既复用了自动生成的类型,又完美匹配Yup的验证规则结构。
2. 给yupResolver添加类型断言
如果不想调整表单类型,直接给解析器加类型断言,让TypeScript认可兼容性:
import { Resolver } from 'react-hook-form'; // ... const formContext = useForm<InventoryItemFormSchema>({ resolver: yupResolver(schema) as Resolver<InventoryItemFormSchema>, values: existingItem });
这种方式快速直接,适合确认Yup规则能保证name字段存在的场景。
3. 扩展自动生成类型创建表单专用类型
创建一个新类型,覆盖原类型的字段定义,适配表单需求:
// 剔除原类型的name,再重新定义为必填,同时添加其他表单字段 type InventoryItemForm = Omit<InventoryItemCreate, 'name'> & { name: string; rfid_id?: string; description?: string; wiki_url?: string; other_owner?: string; model?: string; vendor?: string; };
然后在useForm中使用这个新类型即可:
const formContext = useForm<InventoryItemForm>({ resolver: yupResolver(schema), values: existingItem });
内容的提问来源于stack exchange,提问作者Daniel
相关产品推荐
相关产品推荐

