Card 05/ 09
Iterator in the Wild: Symbol.iterator and for...of in TypeScript
Any object with a Symbol.iterator method works in for...of, spread and destructuring.
TypeScript's for...of loop, the spread operator ... and array destructuring all depend on one method: [Symbol.iterator](), which returns an object with a next method. That pair is the Iterator pattern built into the language, and this card gives a class the method and uses it in each of those places.
class Countdown implements Iterable<number> {
constructor(private readonly from: number) {}
[Symbol.iterator](): Iterator<number> {
let current = this.from;
return {
next: (): IteratorResult<number> =>
current > 0 ? { done: false, value: current-- } : { done: true, value: undefined },
};
}
}
const launch = new Countdown(3);
for (const n of launch) {
console.log("for...of:", n);
}
console.log("spread:", [...launch]);
const [first, second] = launch;
console.log("destructuring:", first, second);
console.log("Array.from:", Array.from(launch));
console.log("arrays use it too:", typeof [][Symbol.iterator]);for...of: 3
for...of: 2
for...of: 1
spread: [ 3, 2, 1 ]
destructuring: 3 2
Array.from: [ 3, 2, 1 ]
arrays use it too: functionCountdown holds a starting number and nothing else; it never builds an array. Each use asks it for a fresh iterator, so for...of, spread, destructuring and Array.from all start again at 3. Destructuring takes only the first two values, and the iterator is never asked for the third.
The last line shows that arrays have the same method, which is why for...of works on them. A Map, a Set and a string have it too.
Python follows the same protocol under different names, __iter__ and __next__. In both languages, an object returns a fresh iterator on request, the iterator has a method the loop calls for the next item, and that method has a way to say the walk is over.
← → move through sections and cards