Home > OS >  Typescript trowing "No index signature" error on optional chaining
Typescript trowing "No index signature" error on optional chaining

Time:09-28

In typescript

const func = (str: string) => {
  const strToNumber = {
    account: 0,
    goals: 1,
    metrics: 2,
  };

  const number = strToNumber?.[str] || 0;
};

gives the following error:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ account: number; goals: number; metrics: number; }'. No index signature with a parameter of type 'string' was found on type '{ account: number; goals: number; metrics: number; }'.

I kinda understand why it is giving this error, but at the same time I don't see anything wrong with the code, since I'm using optional chaining and any falsey value will fallback to 0.

What should I do to not get this error? Is there a way that doesn't involve using "as" or "@ts-ignore"?

CodePudding user response:

You can give the strToNumber object a type like this:

const func = (str: string) => {
  const strToNumber : { [key:string]:number } = { // <--
    account: 0,
    goals: 1,
    metrics: 2,
  };

  const number = strToNumber?.[str] || 0;
};

Here is an example Typescript playground

CodePudding user response:

You probably need to use a guard clause to assert that str is indeed a key of strToNumber, so that accessing strToNumber?.[str] will not throw an error. The guard clause can be something very generic, as such:

function isKeyInShallowObject<T = Record<string, any>>(key: any, obj: T): key is keyof typeof obj {
    return Object.keys(obj).includes(key);
}

Then you can use it as such:

const number = isKeyInShallowObject(str, strToNumber) ? strToNumber[str] || 0;

See proof of concept here. An advantage of this is that you no longer need to rely on optional chaining in this case, i.e. strToNumber[str] will work, since your guard clause will handle the case where str is not a key of the object.


Alternatively, you can let typescript know that strToNumber is just a generic dictionary whose key is any string value:

const func = (str: string) => {
  const strToNumber: Record<string, number> = { ... };

  // Rest of your code here
};

See proof-of-concept.


A dirty but workable hack is a one-liner, where you simply force TypeScript to think that str is somehow a key of strToNumber:

const number = strToNumber?.[str as keyof typeof strToNumber] || 0;

See proof-of-concept.

  • Related