20
votes

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

1

1 Answers

22
votes

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