YouTip LogoYouTip

Ts Iterator Generator

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

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



Why We Need Iterators and Generators

When dealing with collection data, we often need to traverse data structures such as arrays and objects.

Iterators provide a unified, customizable traversal interface that allows any object to be iterated.

Generators are a concise way to create iterators, allowing you to use functions to pause and resume execution, which is very suitable 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 kind of function that can pause during execution and return a value.


Iterable Protocol

Objects that implement the Symbol.iterator method can be traversed by for...of loops.

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;
    console.log("Array element: "+ 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;
    console.log("Character: "+char);

}

Output:

Array element: 1Array element: 2Array element: 3Character: h Character: e Character: l Character: l Character: o

Note: Arrays and strings both have built-in implementations of the Symbol.iterator method, so they can be directly traversed using for...of.


Custom Iterable Objects

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

Example

// Create a custom iterable object: range

var range ={

    from:1,

    to:5,

    // Implement Symbol.iterator method
    [Symbol.iterator]:function(){

        return{

            current:this.from,

            last:this.to,

            // next method returns iteration result
            next:function(){

                if(this.current<=this.last){

                    // Not done, return current value and increment
                    return{ done:false, value:this.current++};

                }

                // Done
                return{ done:true, value:undefined};

            }

        };

    }

};

// Use for...of to iterate

for(var _i =0, range_1 = range; _i < range_1.length; _i++){

    var num = range_1;
    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: using function* syntax

function* numberGenerator(){

    yield 1;// Pause and return 1

    yield 2;// Pause and return 2

    yield 3;// Pause and return 3

}

// Create 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:

First: 1Second: 2Third: 3Done: true

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


Infinite Generator

Generators can produce infinite sequences. Due to lazy evaluation, they do not consume infinite memory.

Example

// Infinite number generator

// Each call only generates one number, not all numbers at once

function* infiniteNumbers(){

    var n =1;

    while (true){// Infinite loop

        yield n++;// Pause and return 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);

// Only get 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:

1st: 12nd: 23rd: 3First 5: 1,2,3,4,5

Lazy Evaluation: The greatest advantage of generators is lazy evaluation. Values are only computed when next() is called, making them very suitable for handling infinite sequences.


Delegating 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: using yield* to delegate

function* combined(){

    yield* gen1();// Delegate to gen1

    yield* gen2();// Delegate to gen2

}

// Iterate over combined generator

for(var _i =0, combined_1 = combined(); _i < combined_1.length; _i++){

    var num = combined_1;
    console.log("Value: "+ num);

}

Output:

Value: 1Value: 2Value: 3Value: 4

yield*: Delegating generators can combine multiple generators or iterable objects, which is very suitable 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 yield type, 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 Practice: 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 using function* and yield
  • yield: Pauses execution and returns a value
  • Delegation: Use yield* to combine multiple generators

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

← Ts PromiseTs Function Overload β†’