简体   繁体   English

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

[英]Typescript: How to defined type of object key for generic type

I have some piece of code like below.我有一些像下面这样的代码。 I write custom hook with generic type for defined custom type return of my hook.我编写了带有泛型类型的自定义钩子,用于定义我的钩子的自定义类型返回。

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;

My generic type T is object.我的通用类型T是对象。 But when I loop over this, I get this error Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'unknown'.但是当我遍历这个时,我得到这个错误Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'unknown'. How can I defined type of key for generic type T ?如何为泛型类型T定义键类型? Please help me.请帮我。

So Object.keys function is not generic parametrized, by this reason v in the forEach is always string.所以Object.keys函数不是通用参数化的,因此 forEach 中的v始终是字符串。 Here is reasoning why - Why doesn't Object.keys return a keyof type in TypeScript?这是推理原因 - 为什么 Object.keys 不返回 TypeScript 中的 keyof 类型? . .

Thats said you cannot narrow type of v it will be just string.那就是说你不能缩小v类型,它只是字符串。 What you can do is type assertion:你可以做的是类型断言:

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

But you will get next error as you don't define that T has all values as strings, and you assign to it empty string.但是您将收到下一个错误,因为您没有将T所有值定义为字符串,并且您将空字符串分配给它。 This is actually correct error.这实际上是正确的错误。 You cannot assign to every field of unknown object a string.您不能为未知对象的每个字段分配一个字符串。

In order to fix the second we need to narrow the type of value of T为了修复第二个我们需要缩小T的值类型

function useInput<T extends Record<string, string>>(): Return<T>

We are saying now T has keys and values which are string or subtypes of string .我们说现在T有钥匙的,哪些是值string或亚型string Because of the fact that they can be subtypes of string we need to do the second assertion after.因为它们可以是字符串的子类型,所以我们需要在之后进行第二个断言。 Why - Subtype can be for example 'a' | 'b'为什么 - 子类型可以是例如'a' | 'b' 'a' | 'b' and such type cannot take empty string value. 'a' | 'b'等类型不能取空字符串值。

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

But without stating that T extends Record<string, string> we would not able to assert property type of such to string .但是如果不说明T extends Record<string, string>我们将无法将此类的属性类型断言为string

As I said before such implementation has an issue, its risky as if you have object which is for example type A { a: 'x' | 'y'}正如我之前所说,这样的实现有问题,它有风险,如果你有对象,例如type A { a: 'x' | 'y'} type A { a: 'x' | 'y'} and you pass it to such construct, then you can get empty string in property a and this is invalid member of type A . type A { a: 'x' | 'y'}并将其传递给这样的构造,然后您可以在属性a获得空字符串,这是A类型A无效成员。 But if you work with types which have string values only, and I assume you do, then its fully ok.但是如果你使用只有string值的类型,我假设你这样做了,那么它完全没问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM