29
votes

Let's say I have this:

const color = {
    red: null,
    green: null,
    blue: null
};

const newColor = ['red', 'green', 'blue'].filter(e => color[e]);

The error is in color[e] near the bottom with:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ red: null; green: null; blue: null; }'. No index signature with a parameter of type 'string' was found on type '{ red: null; green: null; blue: null; }'.

I tried looking everywhere on TypeScript docs, but how the heck am I suppose to interface this so it can accept color[e]?

3

3 Answers

23
votes

The problem you're having is not that color is the wrong type, but that TypeScript is inferring the type of ['red', 'green', 'blue'] to be string[]. Often that type of inference is desirable, since (for all the compiler knows) you might want to push 'purple' onto it. But in this case, you'd like the compiler to know that the only members are the three string literals 'red', 'green', and 'blue'. That is, you need a type at least as specific as Array<'red'|'green'|'blue'>.

Assuming you're using TS3.4 or later, the easiest way to get this kind of type inference from the compiler is to use a const assertion:

const constAssertionTest = ["red", "green", "blue"] as const;
// const constAssertionTest: readonly ["red", "green", "blue"];

The as const causes the compiler to infer a tuple composed of exactly the three string literals in the array, in the exact order you've set. (It's even a read-only tuple). That is good enough to fix your error:

const newColor = (['red', 'green', 'blue'] as const).filter(e => color[e]); // okay

All right, hope that helps. Good luck!

Link to code

48
votes

You can declare colors as any to tell TypeScript to get off your back on this one (aka explicit any):

const color : any = {
    red: null,
    green: null,
    blue: null
};

But if possible, strong typing is preferable:

const color : { [key: string]: any } = {
    red: null,
    green: null,
    blue: null
};

More information on indexing in TypeScript: Index Signatures


EDIT: In this answer to a similar question, the author suggest using a Map<,> -- if that fits your use-case.

3
votes

Thanks for all the great answers. New to typescript and successfully fixed my first road block.

  // in javascript world this is what I used to do.
  let values1 = products.reduce((acc, product) => {
      acc[product] = 1;
  //  ^  --> Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
  // No index signature with a parameter of type 'string' was found on type '{}'.  TS7053

      return acc;
  }, {})

  // in typescript world I needed an extract type specification for 'acc'
  let values2 = products.reduce((acc: { [key: string]: number}, product) => {
      acc[product] = 1; //No more error
      return acc;
  }, {})