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

React Hook Form受控组件封装的TypeScript类型错误排查与解决

React Hook Form 封装 Office UI Fabric TextField 的类型错误修复

问题背景

项目中使用react-hook-form的<Controller>封装office-ui-fabric-react的TextField组件,原有代码运行正常:

<Controller
    control={control}
    name={'Value'}
    rules={{ required: true }}
    render={({ field, fieldState, formState }) => (
      <TextField
        value={field.value}
        errorMessage={fieldState.error?.message}
        label={field.name}
        description={'Value of the property'}
        onChange={(_, value) => field.onChange(value)}
        onBlur={field.onBlur}
        required
      />
    )}
  />

为简化重复代码,希望封装成ControlledTextField组件,用法如下:

<ControlledTextField
    control={control}
    name={'Value'}
    rules={{ required: true }}
    label={'Value'}
    description={'Value of the property'}
    required
/>

现有TypeScript实现代码如下,但编译时报错:Type 'Control<F, any>' is not assignable to type 'Control<FieldValues, any>',错误出现在<Controller>的control={control}处:

type ControllerPropsEx = Omit<ControllerProps, 'render' | 'control' | 'name'>;

// Base type for controlled inputs
type ControlledType<F extends FieldValues, T> = {
  name: Path<F>;
  control: Control<F>;
  controllerProps?: ControllerPropsEx;
} & T;

// props specific to TextField component, combined with controller props 
// and where I remove props actually managed by react hook form
type ControlledTextFieldProps<F extends FieldValues> = ControlledType<
  F,
  Omit<ITextFieldProps, 'onChange' | 'onBlur' | 'value' | 'errorMessage'>
>;

const ControlledTextField = <F extends FieldValues>({
  name,
  control,
  controllerProps,
  ...props
}: ControlledTextFieldProps<F>): JSX.Element => {
  return (
    <Controller
      name={name}
      control={control}
      {...controllerProps}
      render={(
        {
          field: { onBlur, onChange, value, name },
          fieldState: { error }
        }
      ) => (
        <TextField
          {...props}
          name={name}
          value={value || ''}
          onChange={onChange}
          onBlur={onBlur}
          errorMessage={error?.message}
        />
      )}
    />
  );
};

错误原因

ControllerProps默认使用Control<FieldValues, any>类型,而组件泛型F extends FieldValues导致传入的Control<F>与Controller期望的默认类型不兼容。需要让ControllerProps也绑定泛型F,匹配组件的表单字段类型。

解决方案

修改类型定义,让ControllerPropsEx基于带泛型的ControllerProps<F>,同时给<Controller>组件指定泛型F,确保类型完全匹配:

// 调整ControllerPropsEx为带泛型的类型
type ControllerPropsEx<F extends FieldValues> = Omit<ControllerProps<F>, 'render' | 'control' | 'name'>;

// Base type for controlled inputs
type ControlledType<F extends FieldValues, T> = {
  name: Path<F>;
  control: Control<F>;
  controllerProps?: ControllerPropsEx<F>;
} & T;

// props specific to TextField component, combined with controller props 
// and where I remove props actually managed by react hook form
type ControlledTextFieldProps<F extends FieldValues> = ControlledType<
  F,
  Omit<ITextFieldProps, 'onChange' | 'onBlur' | 'value' | 'errorMessage'>
>;

const ControlledTextField = <F extends FieldValues>({
  name,
  control,
  controllerProps,
  ...props
}: ControlledTextFieldProps<F>): JSX.Element => {
  return (
    // 给Controller指定泛型F
    <Controller<F>
      name={name}
      control={control}
      {...controllerProps}
      render={({
        field: { onBlur, onChange, value, name: fieldName },
        fieldState: { error }
      }) => (
        <TextField
          {...props}
          name={fieldName}
          value={value || ''}
          // 适配TextField的onChange参数格式(第二个参数是值)
          onChange={(_, newValue) => onChange(newValue)}
          onBlur={onBlur}
          errorMessage={error?.message}
        />
      )}
    />
  );
};

额外注意:原代码中onChange直接传递给TextField会存在参数不匹配问题(TextField的onChange第一个参数是事件,第二个是值,而react-hook-form的field.onChange只接收值),因此需要调整onChange的处理逻辑,确保参数正确传递。

验证使用

封装后的组件可按预期正常使用,类型检查完全通过:

// 定义表单字段类型
type FormValues = {
  Value: string;
};

// 在表单中使用
const { control } = useForm<FormValues>();

<ControlledTextField<FormValues>
    control={control}
    name={'Value'}
    rules={{ required: '该字段必填' }}
    label={'Value'}
    description={'Value of the property'}
    required
/>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:18:33