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

TypeScript:基于参数类型为React Hook返回数据指定精确类型

useForm Hook的类型定义问题与修复

我编写了如下React Hook:

export const useForm = <T>(values: T) => {
  const [formData, setFormData] = useState<FormFieldData<T>>({});

  useEffect(() => {
    const fields = {};
    for (const key in values) {
      const value = values[key];
      fields[key] = {
        value,
        errorMsg: '',
      };
    }
    setFormData(fields);
  }, []);

  return {
    formData,
  };
};

该Hook接收包含字段及其默认值的对象,例如:

const { formData } = useForm({
  stringField: '',
  numberField: 1,
  booleanField: false,
});

它会基于这些键和默认值构建并返回formData对象,结构如下:

{
  stringField: {
    value: '',
    errorMsg: '',
  },
  numberField: {
    value: 1,
    errorMsg: '',
  },
  booleanField: {
    value: false,
    errorMsg: '',
  },
}

为了给formData定义正确的类型,我使用了FormFieldData泛型类型,基于Hook的泛型参数生成formData的类型:

export type FormField<T> = {
  errorMsg: string;
  value: T;
};

export type FormFieldData<T> = {
  [K in keyof T]: FormField<T[keyof T]>;
};

问题

formData中每个字段对象的value类型变成了传入对象所有类型的联合类型——比如上述示例里,formData.stringField.value的类型为string | number | boolean,但我期望每个字段的value类型与传入Hook时的对应字段原类型一致:formData.stringField.value应为string,formData.numberField.value应为number。

原因

T[keyof T]的含义是:取泛型T所有键对应的类型,合并为联合类型。以上述示例为例,keyof T是"stringField" | "numberField" | "booleanField",T[keyof T]就是T["stringField"] | T["numberField"] | T["booleanField"],也就是string | number | boolean。因此用FormField<T[keyof T]>时,每个字段的value都会被设置成这个联合类型,而非对应键的单独类型。

修复方法

将FormFieldData的定义修改为以下代码,用T[K]替代T[keyof T]:

export type FormFieldData<T> = {
  [K in keyof T]: FormField<T[K]>;
};

这里的K是keyof T中的每个单独键,T[K]就是当前键对应的原类型,这样每个字段的value类型就会和传入时的对应字段类型完全匹配。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 08:43:13