Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Using dynamic keys on object with typescript

I have a function like this

interface Cat {
    color: string,
    weight: number,
    cute: Boolean, // eventhough all cats are cute!
}

export const doSomething = (
    cat: Array<Cat| null>,
    index: number,
    key:  keyof typeof cat,
    payload: string | number | Boolean
) => {
    ....
    cat[key] = payload
    ....
}

This gives me

Element implicitly has an ‘any’ type because expression of type ‘string’ can’t be used to index type

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

Which I understand is because TypeScript thinks that key can be any string instead one of "color", "weight", "cute".
How would I tell in the function declaration that key is one of the three ("color", "weight", "cute")?

I tried

...
key:  keyof Cat,
...

Without luck. This

 cat[key] = payload

Gives me now

Type ‘string| number | Boolean | ‘ is not assignable to type ‘(string & number & Boolean )

>Solution :

cat is an array so keyof typeof cat are the keys of the array not the Cat interface. For those you can use keyof Cat

export const doSomething = (
    cat: Array<Cat | null>,
    index: number,
    key: keyof Cat,
    payload: string | number | Boolean
) => {
    cat.forEach(c => {
        if (c) {
            c[key] = payload
        }
    });
}

This still doesn’t quite work, because there is no correlation between key and payload you could call doSomething(.., ..., 'cute', 1).

You need to add a generic type parameter to tie the payload to the key:

export const doSomething = <K extends keyof Cat>(
    cat: Array<Cat | null>,
    index: number,
    key: K,
    payload: Cat[K]
) => {
    let c = cat[index];
    if (c) {
        c[key] = payload
    }
}

Playground Link

The K in the code above is called a generic type parameter. A function can have generic type parameters in order to capture extra information from the call site making it a generic function. K extends keyof Cat means that K must be a subtype of keyof Cat so it could be one of "color", "weight", "cute".

So when we call doSomething([], 0, "cute", true), K will be "cute". Since we now have the information about which key the function was called with, we can use it in a type query (Cat[K]) to get the actual type of the property with key "cute".

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading