You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 20:09:50