TypeScript Iterators and Generators
Iterators and Generators are powerful features in JavaScript (ES6) that allow you to customize how data is traversed. TypeScript adds type safety to these patterns.
1. Iterators
An object is an Iterator if it knows how to access items from a collection one at a time, while keeping track of its current position within that sequence. It provides a next() method that returns { value: T, done: boolean }.
An object is Iterable if it has a method keyed by Symbol.iterator that returns an Iterator.
Built-in Iterables
Arrays, Strings, Maps, and Sets are built-in iterables.
let numbers = [1, 2, 3];
for (let num of numbers) {
console.log(num);
}
Custom Iterator
You can implement the Iterable interface to make your own objects usable in for...of loops.
class Counter implements Iterable<number> {
constructor(public limit: number) {}
[Symbol.iterator](): Iterator<number> {
let count = 0;
let limit = this.limit;
return {
next(): IteratorResult<number> {
if (count < limit) {
return { done: false, value: ++count };
} else {
return { done: true, value: null as any };
}
}
}
}
}
let counter = new Counter(3);
for (let i of counter) {
console.log(i); // 1, 2, 3
}
2. Generators
Generators provide a much easier way to create iterators. Instead of managing state and implementing next() manually, you write a function that can pause execution using yield.
Syntax
Use an asterisk * after the function keyword.
function* simpleGenerator() {
yield 1;
yield 2;
yield 3;
}
let iterator = simpleGenerator();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true }
3. Typing Generators
TypeScript provides the Generator<T, TReturn, TNext> generic type.
T: The type of values yielded byyield.TReturn: The type of the value returned byreturn(optional, defaults tovoid).TNext: The type of the value passed tonext()(optional, defaults tounknown).
function* generateId(): Generator<number, string, boolean> {
let i = 0;
while (true) {
// 'reset' captures the value passed to next(val)
const reset = yield i++;
if (reset) {
i = 0;
}
if (i > 10) {
return "Limit reached";
}
}
}
const gen = generateId();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next(true).value); // 0 (reset)
4. Async Iterators and Generators
You can also iterate over data that comes asynchronously (like streams).
Async Generators
Use async function* and await inside.
async function* asyncGenerator() {
let i = 0;
while (i < 3) {
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1s
yield i++;
}
}
(async () => {
for await (const num of asyncGenerator()) {
console.log(num);
}
})();