简体   繁体   中英

TypeScript - Element implicitly has an 'any' type because expression of type 'string' can't be used to index type

I have this code:

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 the error:

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 am I supposed to interface this so it can accept color[e] ?

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.

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

I know it is an old question but I'm not satisfied by the other answers. Please avoid the as keyword as much as possible !

Let's see why we encounter this error and what to do with it.

Reason: neither the const nor the array of strings have a type.

When you do not give a type to a const, Typescript infers its type based on the initial value . Knowing that, the error just says "hey, e could be any string, even strings that are not part of the inferred type of your const". What if e was equal to 'yellow' that is not known in color inferred type?

I can recommend you 3 ways to handle this:

1. Simplest but not so " Type script Spirit" solution

Just give a type string to the keys of your const, like this:

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

Hmmm... That works, but we can do better.

2. On the way to types...

Tell Typescript compiler you are using the keys of the inferred type of the const with the keywords: keyof typeof .

In the given example, that would be:

const colorKeys: (keyof typeof color)[]  = ['red', 'green', 'blue'];

const newColor = colorKeys.filter((e) => color[e]);

Can you still make a little extra effort? See next point.

3. Here it is: give types to your objects !

Create an interface (or whatever you need as a type) and give it to your const. Then, specify the type of the array of strings with the keyof keyword:

interface Color {
  red:number|null;
  green:number|null;
  blue:number|null;
}
const color: Color = {
  red: null,
  green: null,
  blue: null
};

const colorKeys: (keyof Color)[]  = ['red', 'green', 'blue'];

const newColor = colorKeys.filter((e) => color[e]);

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;
  }, {})

Here's another solution using Record

const color: Record<string, null> = {
    red: null,
    green: null,
    blue: null
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM