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
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 )
CodePudding user response:
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
}
}
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"
.