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

react-hook-form复用Select组件遇TS2322类型不兼容错误求助

解决react-hook-form与MUI Select组件的TypeScript类型不兼容问题

问题描述

我基于MUI 5.11和react-hook-form实现了一个可复用Select组件,组件代码如下:

...someImports
import { Control, Controller } from 'react-hook-form';

interface SelectProps {
  control: Control;
 ...someProps
}

export interface SelectOption {
  title: string;
  value: any;
}

const Select: FC<SelectProps> = ({
  ...props
}) => {
  return (
    <Controller
      control={control}
      name={name}
      render={({ field }) => (
        <FormControl {...props} fullWidth>
          <InputLabel id={label}>{label}</InputLabel>
          <MuiSelect
            ..someOptions
          >
            {options.map((option) => (
              <MenuItem value={option.value}>{option.title}</MenuItem>
            ))}
          </MuiSelect>
        </FormControl>
      )}
    />
  );
};

export default Select;

在使用该组件时触发TypeScript错误:

TS2322: Type 'Control<FormFields, any>' is not assignable to type 'Control<FieldValues, any>'.

使用场景代码:

interface FormFields {
  name: string;
  age: number;
}

const { control, handleSubmit, setValue, watch } = useForm<FormFields>()

<Grid container columnGap={5} alignItems="stretch" sx={{ width: '500px' }}>
      <Grid xs={4}>
        <Select
          control={control} // 此处control出现类型报错

解决方案

问题根源在于Select组件的Control类型未做泛型约束,默认使用Control<FieldValues>,与使用时传入的Control<FormFields>类型不匹配。通过给组件添加泛型支持即可解决:

  1. 修改SelectProps接口,添加泛型约束
import { Control, Controller, FieldValues } from 'react-hook-form';

// 新增泛型T,约束表单字段类型
interface SelectProps<T extends FieldValues> {
  control: Control<T>;
  name: keyof T; // 约束name必须是表单字段的键,提升类型安全性
  label: string;
  options: SelectOption[];
  // 其他自定义props
}

export interface SelectOption {
  title: string;
  value: any;
}
  1. 让Select组件支持泛型
// 组件定义时接收泛型T
const Select = <T extends FieldValues>({
  control,
  name,
  label,
  options,
  ...props
}: SelectProps<T>) => {
  return (
    <Controller
      control={control}
      name={name}
      render={({ field }) => (
        <FormControl {...props} fullWidth>
          <InputLabel id={`${label}-id`}>{label}</InputLabel>
          <MuiSelect
            labelId={`${label}-id`}
            {...field}
            label={label}
          >
            {options.map((option) => (
              <MenuItem key={option.value} value={option.value}>
                {option.title}
              </MenuItem>
            ))}
          </MuiSelect>
        </FormControl>
      )}
    />
  );
};

export default Select;
  1. 使用组件(自动推断或显式指定泛型)
    TypeScript会自动从useForm<FormFields>的control推断出泛型类型,也可以显式指定:
<Select<FormFields>
  control={control}
  name="age" // 此时name会被约束为FormFields的键(name/age),避免拼写错误
  label="年龄"
  options={[
    { title: "18", value: 18 },
    { title: "20", value: 20 }
  ]}
/>

修改后,组件的control类型会与使用时的表单字段类型保持一致,TypeScript报错即可消除。

内容的提问来源于stack exchange,提问作者PttRulez

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 11:45:21