我有一些类似下面的代码。我用通用类型编写自定义钩子,以定义我的钩子的自定义类型返回。
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
定义键的类型?请帮助我。
答案 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
的键和值是string
或string
的子类型。由于它们可以是字符串的子类型,因此我们需要在其后进行第二个声明。原因-子类型可以是'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
值的类型,并且我认为您可以使用,则完全可以。