TypeScript Iterators and Generators

Iterators and generators are important patterns in JavaScript/TypeScript for working with collections.

They provide a unified way to iterate over data, making it easier to handle large data streams, infinite sequences, and more.


SVG Diagram: Iterators and Generators Background Title Iterator and Generator Workflow Iterator Protocol Iterator Protocol Implement Symbol.iterator Return next() method { done, value } Arrow for...of loop for...of loop Automatically calls next() Iterates over all elements Stops when done=true Arrow Generator Generator function* yield pauses Lazy evaluation Bottom section: Generator characteristics Generator Characteristics Feature 1 Lazy evaluation - generates on demand Feature 2 State preservation - pause position Feature 3 Composable - yield* delegation Arrow marker

Why Do We Need Iterators and Generators

When working with collection data, we often need to iterate over data structures such as arrays and objects.

Iterators provide a unified, customizable iteration interface, allowing any object to be iterated over.

Generators are a concise way to create iterators. They allow you to pause and resume execution using functions, making them ideal for handling large data streams or infinite sequences.

Concept explanation:An iterator is an object that provides a next() method for traversing data. A generator is a special function that can pause during execution and return a value.


Iterable Protocol

Objects that implement the Symbol.iterator method can be iterated over with a for...of loop.

Example

// Arrays are iterable by default
var arr = [1, 2, 3];
for (var _i = 0, arr_1 = arr; _i < arr_1.length; _i++) {
    var item = arr_1[_i];
    console.log("Array elements: " + item);
}

// Strings are iterable by default
var str = "hello";
for (var _i = 0, str_1 = str; _i < str_1.length; _i++) {
    var char = str_1[_i];
    console.log("Characters: " + char);
}

Output:

数组元素: 1
数组元素: 2
数组元素: 3
字符: h
字符: e
字符: l
字符: l
字符: o

Explanation:Arrays and strings both have built-in Symbol.iterator implementations, so they can be iterated over directly with for...of.


Custom Iterable Objects

Make ordinary objects implement the Symbol.iterator interface so they can be iterated over.

Example

// Create a custom iterable object: range
var range = {
    from: 1,
    to: 5,
    // Implement the Symbol.iterator method
    [Symbol.iterator]: function() {
        return {
            current: this.from,
            last: this.to,
            // The next method returns the iteration result
            next: function() {
                if (this.current <= this.last) {
                    // Not done, return the current value and increment
                    return { done: false, value: this.current++ };
                }
                // Done
                return { done: true, value: undefined };
            }
        };
    }
};

// Iterate with for...of
for (var _i = 0, range_1 = range; _i < range_1.length; _i++) {
    var num = range_1[_i];
    console.log("Range: " + num);
}

Iterator Protocol:An iterator must have a next() method that returns an object in the format { done: boolean, value: any }.


Generator Functions

Use the function* syntax to create generators, and use yield to pause execution and return a value.

Example

// Generator function: uses function* syntax
function* numberGenerator() {
    yield 1;  // Pause and return 1
    yield 2;  // Pause and return 2
    yield 3;  // Pause and return 3
}

// Create a generator instance
var gen = numberGenerator();

// Each call to next() executes until the next yield
console.log("First: " + gen.next().value);
console.log("Second: " + gen.next().value);
console.log("Third: " + gen.next().value);
console.log("Done: " + gen.next().done);

Output:

第一个: 1
第二个: 2
第三个: 3
完成: true

Generator:A generator function returns an iterator, and each call to next() executes until the next yield statement.


Infinite Generators

Generators can produce infinite sequences. Since they use lazy evaluation, they do not consume infinite memory.

Example

// Infinite number generator
// Each call generates only one number, not all numbers at once
function* infiniteNumbers() {
    var n = 1;
    while (true) {  // Infinite loop
        yield n++;   // Pause and return the current value, then increment
    }
}

var gen = infiniteNumbers();
console.log("1st: " + gen.next().value);
console.log("2nd: " + gen.next().value);
console.log("3rd: " + gen.next().value);

// Get only the first 5 numbers
var nums = [];
var iter = infiniteNumbers();
for (var i = 0; i < 5; i++) {
    nums.push(iter.next().value);
}
console.log("First 5: " + nums);

Output:

第1个: 1
第2个: 2
第3个: 3
前5个: 1,2,3,4,5

Lazy evaluation:The biggest advantage of generators is lazy evaluation: the next value is only computed when next() is called, making them ideal for handling infinite sequences.


Delegated Generators

Use yield* to delegate to another generator or iterable object.

Example

// First generator
function* gen1() {
    yield 1;
    yield 2;
}

// Second generator
function* gen2() {
    yield 3;
    yield 4;
}

// Combined generator: uses yield* delegation
function* combined() {
    yield* gen1();  // Delegate to gen1
    yield* gen2();  // Delegate to gen2
}

// Iterate over the combined generator
for (var _i = 0, combined_1 = combined(); _i < combined_1.length; _i++) {
    var num = combined_1[_i];
    console.log("Value: " + num);
}

Output:

值: 1
值: 2
值: 3
值: 4

yield*:Delegated generators can combine multiple generators or iterable objects, making them well-suited for building reusable data streams.


TypeScript Generator Types

Generator type annotations use the Generator type.

Example

// Generator type: Generator<yield type, return type, next parameter type>
function* idGenerator(): Generator<number, void, unknown> {
    var i = 1;
    while (i <= 3) {
        yield i++;  // yield number type
    }
    // return void
}

var gen = idGenerator();
console.log(Array.from(gen));

Type explanation:Generator<T, R, N> means: T is the type of yield, R is the final return type, and N is the type of the next() parameter.


Notes

  • Iterator protocol:Implement Symbol.iterator to return an object with a next() method
  • Generator syntax:Use function* instead of function
  • yield keyword:Pauses execution and returns a value
  • Lazy evaluation:Generators compute on demand and do not generate all values at once
Best practices:Use generators when handling large data streams, infinite sequences, or scenarios that require pause/resume.

Summary

Iterators and generators are powerful data processing tools in TypeScript.

  • Iterable objects:Implement the Symbol.iterator interface
  • Generators:Created with function* and yield
  • yield:Pause execution and return values
  • Delegation:Use yield* to combine multiple generators

Recommendation:Use iterators and generators when you need to iterate over custom objects, process data streams, or create infinite sequences.

Other Extensions