”;
In TypeScript, iterators and generators allow to control the iteration over the iterables. Here, iterables are objects like arrays, tuples, etc. through which we can iterate. Using iterators and generators in the code allows us to write efficient and readable code.
Here, we will discuss how to create custom iterators and generators in TypeScript.
Iterators
Iterators are used to traverse through the iterable objects. It is a unique function that returns the iterator object. The iterator object contains the next() method, which again returns the object having below 2 properties.
-
value: The value property contains the value of the next element in the sequence.
-
done: The done property contains the boolean value, representing whether the iterator reaches the end of the sequence or not.
Let”s look at the below examples of iterators.
Example: Using the values() Method
In the code below, we have defined the ”fruits” array containing the string. The ”fruits.values()” returns an iterator object, which is stored in the ”iterator” variable.
Whenever we call the next() method, it returns the object containing the ”value” and ”done” properties. You can see all the values of the array. Whenever we call the next() method 6th time, it returns the object having ”value” property with an undefined value as iterator reached to the end of the sequence.
// Defining a fruits array const fruits = [''apple'', ''banana'', ''mango'', ''orange'', ''strawberry'']; // Defining an iterator const iterator = fruits.values(); // Getting the first element console.log(iterator.next().value); // apple // Getting the second element console.log(iterator.next().value); // banana // Getting remaining elements console.log(iterator.next().value); // mango console.log(iterator.next().value); // orange console.log(iterator.next().value); // strawberry console.log(iterator.next().value); // undefined
On compiling, it will generate the following JavaScript code.
// Defining a fruits array const fruits = [''apple'', ''banana'', ''mango'', ''orange'', ''strawberry'']; // Defining an iterator const iterator = fruits.values(); // Getting the first element console.log(iterator.next().value); // apple // Getting the second element console.log(iterator.next().value); // banana // Getting remaining elements console.log(iterator.next().value); // mango console.log(iterator.next().value); // orange console.log(iterator.next().value); // strawberry console.log(iterator.next().value); // undefined
Output
The output of the above example code is as follow –
apple banana mango orange strawberry undefined
Example: Creating the Custom Iterator Function
In the code below, createArrayIterator() function is a custom iterator function.
We have started with defining the ”currentIndex” variable to keep track of the index of the element in the iterable.
After that, we return the object containing the ”next” property from the function. The ”next” property contains the method as a value, which returns the object containing the ”value” and ”done” property. The assigned value into the ”value” and ”done” properties is based on the current element in the sequence.
After that, we used the createArrayIterator() function to traverse through the array of numbers.
// Custom iterator function function createArrayIterator(array: number[]) { // Start at the beginning of the array let currentIndex = 0; // Return an object with a next method return { // next method returns an object with a value and done property next: function () { // Return the current element and increment the index return currentIndex < array.length ? { value: array[currentIndex++], done: false } : { value: null, done: true }; } }; } // Create an iterator for an array of numbers const numbers = [10, 20, 30]; const iterator = createArrayIterator(numbers); console.log(iterator.next().value); // 10 console.log(iterator.next().value); // 20 console.log(iterator.next().value); // 30 console.log(iterator.next().done); // true
On compiling, it will generate the following JavaScript code.
// Custom iterator function function createArrayIterator(array) { // Start at the beginning of the array let currentIndex = 0; // Return an object with a next method return { // next method returns an object with a value and done property next: function () { // Return the current element and increment the index return currentIndex < array.length ? { value: array[currentIndex++], done: false } : { value: null, done: true }; } }; } // Create an iterator for an array of numbers const numbers = [10, 20, 30]; const iterator = createArrayIterator(numbers); console.log(iterator.next().value); // 10 console.log(iterator.next().value); // 20 console.log(iterator.next().value); // 30 console.log(iterator.next().done); // true
Output
The output of the above example code is as follows –
10 20 30 true
Generators
Generator functions are also similar to the iterators, which return the values one by one rather than returning all values once. When you call the generator function, that returns the generator object which can be used to get values one by one.
Generator functions are mainly useful when you want to get values one by one rather than getting all values at once and storing them in the memory.
Syntax
Users can follow the syntax below to create generator function in TypeScript.
function* func_name() { yield val; } const gen = numberGenerator(); // "Generator { }" console.log(gen.next().value); // {value: val, done: false}
-
In the above syntax, we have used the ”function*” to define the generator function.
-
You can use the ”Yield” keyword to return values one by one from the generator function.
-
When you call the generator function, it returns the generator object.
-
When you call the next() method, it returns the object containing the ”value” and ”done” properties same as the iterator.
Example: Basic Generator Function
In the code below, the numberGenerator() function is a generator function. We have used the ”yield” keyword and returned 10, 20, and 30 values one by one.
After that, we called the numberGenerator() function which returns the generator object. To get the values, we use the next() method of the generator object.
// Basic generator function function* numberGenerator() { yield 10; yield 20; yield 30; } // Create a generator object const gen = numberGenerator(); // Call the generator function console.log(gen.next().value); // 10 console.log(gen.next().value); // 20 console.log(gen.next().value); // 30 console.log(gen.next().done); // true
On compiling, it will generate the same JavaScript code.
Output
The output of the above example code is as follows –
10 20 30 true
Example: Creating the Generator Function to Traverse a Range
Here, we have defined the range() generator function which takes the starting and ending point of the range as a parameter. In the function, we traverse the range and return the values one by one using the ”yield” keyword.
After that, we used the range() function with the ”for loop” to traverse the generator object returned from the range() function. The loop prints each value returned from the range() function.
// Generators are functions that allow to traverse a range function* range(start: number, end: number) { // Loop through the range for (let i = start; i <= end; i++) { // Yield the current value yield i; } } // Loop through the range for (const num of range(1, 5)) { console.log(num); // 1, 2, 3, 4, 5 }
On compiling, it will generate the following JavaScript code.
// Generators are functions that allow to traverse a range function* range(start, end) { // Loop through the range for (let i = start; i <= end; i++) { // Yield the current value yield i; } } // Loop through the range for (const num of range(1, 5)) { console.log(num); // 1, 2, 3, 4, 5 }
Output
1 2 3 4 5
Difference Between Iterators and Generators
Iterators and generators look similar. However, they are different. Here, we have explained some differences between both.
Feature | Iterator | Generator |
---|---|---|
Definition | An object that adheres to the Iterator protocol, specifically implementing a next() method. | A function that can pause execution and resume, automatically managing the state internally. |
Control Mechanism | Manually controls iteration via the next() method, which returns { value, done }. | Uses yield to pause and return values, and next() to resume. |
Syntax | Typically involves creating an object with a next() method. | Defined with function* syntax and includes one or more yield statements. |
Usage Complexity | Higher, due to explicit state management and the need for a custom next() implementation. | Lower, as state management and iteration control are simplified by yield. |
Ideal Use Cases | Suitable for simple, custom iterations where explicit control is required. | Better for complex sequences, asynchronous tasks, or when leveraging lazy execution. |
”;