打字稿:如何为通用类型定义对象键的类型

时间:2020-02-14 11:05:12

标签: reactjs typescript

我有一些类似下面的代码。我用通用类型编写自定义钩子,以定义我的钩子的自定义类型返回。

type Return<T> = [T, (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => void, Record<keyof T, boolean>, () => void]


function useInput<T>(): Return<T> {
    const [input, setInput] = useState<T>({} as T);
    const [isDirty, setDirty] = useState({} as Record<keyof T, boolean>);

    const handleInputChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
        setInput({
            ...input,
            [e.target.name]: e.target.value
        });
        setDirty({
            ...isDirty,
            [e.target.name]: true
        });
    };

    const resetInput = () => {
        Object.keys(input).forEach((v) => input[v] = ''); //this line i get error
        setInput({...input});
    };


    return [input, handleInputChange, isDirty, resetInput]
}

export default useInput;

我的通用类型T是对象。但是,当我遍历此错误时,出现此错误Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'unknown'.,如何为通用类型T定义键的类型?请帮助我。

1 个答案:

答案 0 :(得分:1)

因此Object.keys函数不是通用参数,因此forEach中的v始终是字符串。这是为什么-Why doesn't Object.keys return a keyof type in TypeScript?

也就是说,您不能缩小v的类型,它只会是字符串。您可以做的是断言:

 Object.keys(input).forEach((v) => input[v as keyof T] = '')

但是您将得到下一个错误,因为您没有定义T的所有值都为字符串,而是将其分配为空字符串。这实际上是正确的错误。您不能为未知对象的每个字段分配字符串。

为了解决第二个问题,我们需要缩小T

的值类型
function useInput<T extends Record<string, string>>(): Return<T>

我们现在说T的键和值是stringstring的子类型。由于它们可以是字符串的子类型,因此我们需要在其后进行第二个声明。原因-子类型可以是'a' | 'b',而这种类型不能采用空字符串值。

Object.keys(input).forEach((v) => (input[v as keyof T] as string) = '')

但是如果没有说明T extends Record<string, string>,我们将无法断言string这样的属性类型。

正如我在实现之前遇到的问题之前所说的那样,它冒着风险,就好像您有对象,例如type A { a: 'x' | 'y'}并将其传递给这样的构造,那么您可以在属性a中获得空字符串这是类型A的无效成员。但是,如果您使用仅具有string值的类型,并且我认为您可以使用,则完全可以。