Suppose I have interface
interface X {
a: string;
b: number;
c: boolean;
}
and a function
function values(x: X) {
return Object.keys(x).map(s => x[s])
}
When I enable typescript's strict
flag I get the error "Element implicitly has an 'any' type because type 'X' has no index signature". So to make it explicit, I can just add an index signature to the definition of X
[key: string]: any;
Easy peasy.
However if I X is now a mapped type instead:
type X<T> = {
[P in keyof T]: string;
}
and I have the function
function values<T>(x: X<T>) {
return Object.keys(x).map(s => x[s])
}
where am I supposed to add the index signature? Is there any way to make this explicit without resorting to doing something gross like Object.keys(x).map(s => (x as any)[s])
Index signature syntax The syntax of an index signature is pretty simple and looks similar to the syntax of a property, but with one difference. Instead of the property name, you simply write the type of the key inside the square brackets: { [key: KeyType]: ValueType } .
The error "Property is incompatible with index signature" occurs when a property is not compatible with the specified type of the index signature. To solve the error, change the type of the property or use a union to update the type in the index signature.
A mapped type is a generic type which uses a union of PropertyKey s (frequently created via a keyof ) to iterate through keys to create a type: type OptionsFlags < Type > = { [ Property in keyof Type ]: boolean; };
The indexing type is itself a type, so we can use unions, keyof , or other types entirely: type I1 = Person ["age" | "name"]; type I1 = string | number. type I2 = Person [keyof Person ]; type I2 = string | number | boolean.
You can:
interface X {
a: string;
b: number;
c: boolean;
[key: string]: X[keyof X];
}
The result of X[keyof X]
will now be (string | number | boolean)
, which works even better than any
because the return of your function will be (string | number | boolean)[]
.
Example
Another way that should work with both examples is:
function values(x: X) {
const keys = Object.keys(x) as (keyof X)[];
return keys.map(s => x[s]);
}
Not pretty, but at least more typed than (x as any)
.
Of course it can be made generic too:
function values<T>(x: T) {
const keys = Object.keys(x) as (keyof T)[];
return keys.map(s => x[s]);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With