首页 > 解决方案 > 打字稿。定义泛型键作为字符串

问题描述

我想获取一些对象并通过键将其修改为字符串并返回对象。

const useInputs = <T extends Record<string, string>>(defaultValue: T) => {
    const [inputsData, setInputsData] = useState<T>(defaultValue)

    type UpdateFunction = (key: string) => (el: React.FocusEvent<HTMLInputElement>) => void
    const updateData: UpdateFunction = key => el => {
        const newInputsData = {...inputsData}
        newInputsData[key] = el.target.value
        setInputsData(newInputsData)
    }

    return { inputsData, updateData }
}

但我对newInputsData[key] = el.target.value “类型'字符串'不能用于索引类型'T'.ts(2536)”有错误

我该如何解决这个问题?谢谢

标签: typescriptreact-hookstypescript-typings

解决方案


出现该错误是因为泛型类型T可以使用扩展的任何类型实例化Record<string, string>

考虑类型

interface Inputs {x: string; y: string} 

虽然Inputsextends Record<string, string>,允许使用任意字符串对其进行索引是完全不安全的。

换句话说,我们不想要以下

const i: Inputs = {x: "A", y: "B"};
const [inputs, setInputs] = useInputs(i);
setInputs("name")(event);

因为Inputs没有name财产。

因此,您的内部类型 ,UpdateFunction必须采用一个参数,该参数是T任何实例化类型的有效键。这样的类型写成keyof Tin (key: keyof T) => (el: React.FocusEvent<HTMLInputElement>) => void

放在一起应该是这样的

const useInputs = <T extends Record<string, string>>(defaultValue: T) => {
    const [inputsData, setInputsData] = useState<T>(defaultValue);

    type UpdateFunction = (key: keyof T) => (el: {target: {value: T[keyof T]}}) => void;
    const updateData: UpdateFunction = key => el => {
        const newInputsData = {...inputsData};
        newInputsData[key] = el.target.value;
        setInputsData(newInputsData)
    };

    return [inputsData, updateData] as const;
};

注意外部函数的返回值的调整,它允许常规使用你的钩子。

const [inputs, setInputs] = useInputs(...);

与非正统相反

const {inputsData, updateData} = useInputs(...);

此外,请注意,为了进行上述类型检查,我们还必须调整第二个参数的类型el,它提供了使用 from React.FocusEvent<HTMLInputElement>to更新属性的值,{target: {value: T[keyof T]}}原因与上面讨论的相同,泛型T可以被实例化具有其string诸如"despair"和的子类型的属性"elation"

替代方法包括使用更精确的类型,el例如

Omit<React.FocusEvent<HTMLInputElement>, "target"> & {
  target: { value: T[keyof T] };
}

移动更改方式 useInput是通用的,从整个输入对象的类型到该对象的属性键。

const useInputs = <K extends string>(defaultValue: Record<K, string>) => {
  const [inputsData, setInputsData] = useState(defaultValue);

  type UpdateFunction = (
    key: K
  ) => (el: React.FocusEvent<HTMLInputElement>) => void;
  const updateData: UpdateFunction = key => el => {
    const newInputsData = { ...inputsData };
    newInputsData[key] = el.target.value;
    setInputsData(newInputsData);
  };

  return [inputsData, updateData] as const;
};

我更喜欢最后一个,因为HTMLInputElement'svalue总是一个string.


推荐阅读