Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I add an index signature for a mapped type?

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])

like image 987
Stuart Avatar asked Nov 24 '17 00:11

Stuart


People also ask

How do I create a index signature in TypeScript?

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 } .

Is incompatible with index signature?

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.

What is a mapped object type?

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; };

What is index type in TypeScript?

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.


1 Answers

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]);
}
like image 180
Meligy Avatar answered Oct 22 '22 21:10

Meligy