11

How do I loop over an array of tuples in TypeScript? e.g.

for (const [x, y] of [['a', 1], ['b', 2]]) {
  y + 1;
}

complains:

error TS2365: Operator '+' cannot be applied to types 'string | number' and '1'.

If I understand correctly, TypeScript infers type (string | number)[][] for the loop expression, which is why the loop variable y has type string | number although actually it can only ever have type number?

I think https://github.com/microsoft/TypeScript/issues/3369 is the issue that keeps TypeScript from inferring the suitable type. What is the current solution for looping over an array of tuples? Type assertions?

2 Answers 2

9

Just add type annotations that TS should understand the structure. It can't infer from the set yet.

const array: [string, number][] = [['a', 1], ['b', 2]];

for (const [x, y] of array) {
  y + 1;
}

Additionally I like to mention that when dealing with 2 dimensional associations, the better data structure is a Map in my opinion:

const myMap = new Map<string, number>([['a', 1], ['b', 2]]);

for (const [x, y] of [...myMap]) {
  console.log(y + 1);
}

Advanced: Custom iterables

If the logic for the tuples is something consistent, then you can create your own iterable objects by using the Symbol.iterator famous symbol:

class TupleMaker implements Iterable<[string, number]> {
  private next = 0;
  constructor(private endsAt: number = 0) {}

  private increment(): void {
    this.next++;
  }

  *[Symbol.iterator](): Generator<[string, number]> {
    const alpha = Array.from(Array(26)).map((e, i) => i + 65);
    const alphabet = alpha.map((x) => String.fromCharCode(x).toLocaleLowerCase());
    while (this.next < this.endsAt) {
      yield [alphabet[this.next], this.next + 1];
      this.increment();
    }
  }
}

for (const [x, y] of new TupleMaker(13)) {
  console.log(y + 1);
}

They can be async too using Symbol.asyncIterator

Sign up to request clarification or add additional context in comments.

2 Comments

Re: Map, assuming xs are distinct.
Yes, That's what I assumed based on a,b,c
6

The real issue is that the type of [['a', 1], ['b', 2]] is not a tuple type at all. It will be the array type (string | number)[][]. So when destructuring x and y will both be string | number. Typescript will only infer tuple in particular situations (like a type parameter constrained to an array, or an as const assertion).

If you use an as const assertion to get typescript to infer a tuple type and everything will work as expected:

for (const [x, y] of [['a', 1], ['b', 2]] as const) {
  y + 1;
}

Play

1 Comment

Awesome! I was unaware of const assertions: I've now added as const and readonly [...]/readonly ...[] to a bunch of code. Thank you! BTW devblogs.microsoft.com/typescript/announcing-typescript-3-4/… was the best resource I found on const assertions the readonly type modifier.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.