Home > Mobile >  Typescript: do I have to check for array to get rid of TS7053 (string index to possible array elemen
Typescript: do I have to check for array to get rid of TS7053 (string index to possible array elemen

Time:01-19

I have run into a situation where I believe obeying typescript checks produces slower code.

So, here's my (oversimplified) function:

function test(data: { [key: string]: any } | any[]) {
  const x = Object.keys(data).reduce((res: { [key: string]: any }, name: string) => {
    const item = data[name];
    const key = item?.name || name;
    ...
  });
}

basically just converts either array or object into an object - when looked simplified like this. In reality it also makes sure that items are of correct class type.

Anyway, the data[name] line produces a TS7053, because - if data is the array - accessor should be a Number.

The solution to typescript warning is to rewrite the line like this:

const item = data instanceof Array ? data[Number(name)] : data[name];

However, I believe this solution is slower than implicit type coercion that happened before for instances where the parameter was an array. It first checks for type, branches and only then does the explicit type conversion and finally accesses the item.

I realize that in case of array, a much more proper solution would be to simply use Array.forEach, but that would require a function extraction to not duplicate the code in question, which is only three lines in its entirety. The intent is just to index the items by their name property for faster access.

Is there a better way to make TypeScript checker happy?

CodePudding user response:

You could use Object.entries() instead of Object.keys(). This saves you from having to do the value lookup and fixes your type problem at the same time:

function test(data: { [key: string]: any } | any[]) {
  return Object.entries(data).reduce((a: { [key: string]: any }, [k, v]) => {
    a[v?.name || k] = v;
    return a;
  }, {});
}

Playground link

CodePudding user response:

An array is also an object, so you can simply omit that from the type signature:

function test(data: { [key: string]: any }) {
  const x = Object.keys(data).reduce((res: { [key: string]: any }, name: string) => {
    const item = data[name];
    const key = item?.name || name;
    res[key] = item;
    return res;
  }, {});
  console.log(x);
}

test([1, 2, 3]);               // { "0": 1, "1": 2, "2": 3 }
test({a: 'Alice', b: 'Bob'});  // { "a": "Alice", "b": "Bob" }  

Playground link

This works because Object.keys always returns keys as strings, even if they are numeric array keys, and because you can also use numeric strings to index arrays.

I also had to add an empty object {} as the initial value for reduce, otherwise it will take the first key, which is a string instead of an object.

  • Related