Learn Typescript Generators

Introduction

TypeScript is a typed superset of JavaScript that brings static type checking to JavaScript development. TypeScript adds some much-needed structure and safety to JavaScript programming by providing tools for catching errors at compile time instead of run time. A generator is a feature that TypeScript borrows from Python. Generators are a special kind of function that can pause its execution and later continue from where it left off. The use of Generators has significantly increased over the years in Typescript programming.

In this article, we will explore Typescript Generators. We will take a deep dive into what Generators are, how they differ from other functions, and how they can be used in TypeScript programming. We will also explore advanced Generator techniques and the benefits of using Generators in Typescript programming.

Understanding Generators

Generators are special kinds of functions in TypeScript that allow you to create sequences of values that can be iterated over. A Generator function is defined using the function* keyword followed by the function name. Generators work differently from the standard TypeScript functions. When a standard function is called, it runs to completion and returns a value to the caller. However, when a generator is called, it returns an iterator object and starts execution.

Internally, a generator function creates a generator object that implements the iterable protocol. This object can be used to iterate over a sequence of values generated by the function. The iterable protocol is a standard protocol in JavaScript that defines how to create an object that can be iterated over using the for..of loop. This protocol requires the object to have a [Symbol.iterator] property that returns an object with a next() method.

One of the main features of Generators is the ability to pause and resume their execution. This feature opens up new possibilities for creating complex sequences of values that can be generated over time. The yield keyword is used in Generators to pause the execution and return a value to the caller. When the function is called again, it continues from the point where it left off.

Syntax and Structure

As mentioned earlier, Generator functions use the function* keyword. Here’s an example of a simple generator function that generates a sequence of numbers from 1 to 3.

function* simpleGenerator() {
  yield 1;
  yield 2;
  yield 3;
}

We can call this function and iterate over the sequence of numbers generated using the for..of loop.

for (let value of simpleGenerator()) {
  console.log(value);
}
// Output: 1, 2, 3

As we can see, the for..of loop can be used to iterate over the sequence of values generated by the simpleGenerator() function.

How they differ from other functions

Generators have several key differences from other functions in TypeScript. The first difference is in the way they are called. When other functions are called, they run to completion and the result is returned to the caller. However, when a Generator is called, it returns an iterator object that can be used to iterate over the sequence of values generated by the function.

Another difference is in the way Generators can pause and resume their execution. This feature allows us to generate complex sequences of values that can be generated over time. The yield keyword is used to pause the execution and return a value to the caller. When the function is called again, it continues from the point where it left off.

Generators can also be used to provide lazy computation. This means that values are only generated when they are needed. This is useful in scenarios where we need to generate a large sequence of values and we don’t want to generate all of them upfront.

Using Generators in Typescript

Now that we have a basic understanding of Generators, let’s see how we can create a simple Generator function in TypeScript. In this example, we will create a function that generates a sequence of even numbers.

function* evenNumbers() {
  let number = 0;
  while (true) {
    number += 2;
    yield number;
  }
}

In this example, we first set number to 0, and then enter an infinite loop. Inside the loop, we increment number by 2 and use the yield keyword to return number. This creates a sequence of even numbers starting from 2.

We can now call this function and iterate over the sequence of even numbers using the for..of loop.

for (let value of evenNumbers()) {
  if (value > 10) break;
  console.log(value);
}
// Output: 2, 4, 6, 8, 10

As we can see, the sequence of even numbers is generated lazily. Only the values that are needed are generated.

Yielding values in Generators

The yield keyword is used in Generators to pause the execution and return a value to the caller. When the function is called again, it continues from the point where it left off.

In the following example, we create a Generator function that generates a sequence of Fibonacci numbers.

function* fibonacci() {
  let current = 0;
  let next = 1;
  while (true) {
    let reset = yield current;
    [current, next] = [next, current + next];
    if (reset) {
      current = 0;
      next = 1;
    }
  }
}

In this example, we first set current = 0 and next = 1. We then enter an infinite loop that generates the next Fibonacci number using current + next. We use the yield keyword to return the current Fibonacci number to the caller.

We can now call this function and iterate over the sequence of Fibonacci numbers.

let sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3

As we can see, each time we call sequence.next().value, we get the next Fibonacci number in the sequence.

Iterating over the values using the for..of loop

The for..of loop can be used to iterate over the sequence of values generated by a Generator function. Here’s an example of how we can use for..of to iterate over the sequence of even numbers generated by the evenNumbers() function.

for (let value of evenNumbers()) {
  if (value > 10) break;
  console.log(value);
}
// Output: 2, 4, 6, 8, 10

The for..of loop can also be used to simplify the iteration over the sequence of Fibonacci numbers generated by the fibonacci() function.

for (let value of fibonacci()) {
  if (value > 50) break;
  console.log(value);
}
// Output: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34

Advanced Generators Techniques

Passing values to Generators

We can pass values to a Generator using the next() method. In the following example, we create a Generator function that expects a reset value to be passed in.

function* fibonacci() {
  let current = 0;
  let next = 1;
  while (true) {
    let reset = yield current;
    if (reset) {
      current = 0;
      next = 1;
    }
    [current, next] = [next, current + next];
  }
}

We can now call this function and pass a reset value of true to reset the sequence.

let sequence = fibonacci();
console.log(sequence.next().value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 2
console.log(sequence.next().value); // 3
console.log(sequence.next(true).value); // 0
console.log(sequence.next().value); // 1
console.log(sequence.next().value); // 1

As we can see, we pass in true to reset the sequence.

Combining Generators

We can combine multiple Generators to create a new Generator. In the following example, we combine two Generators to create a new Generator that generates a sequence of both even and odd numbers.

function* evenNumbers() {
  let number = 0;
  while (true) {
    number += 2;
    yield number;
  }
}

function* oddNumbers() {
  let number = -1;
  while (true) {
    number += 2;
    yield number;
  }
}

function* evenAndOddNumbers() {
  let generator1 = evenNumbers();
  let generator2 = oddNumbers();
  while (true) {
    yield generator1.next().value;
    yield generator2.next().value;
  }
}

In this example, we create two Generator functions that generate a sequence of even and odd numbers. We then create a new Generator function that combines the sequences generated by the evenNumbers() and oddNumbers() functions using the yield keyword.

We can now call this new Generator function and get a sequence of even and odd numbers.


for (let value of evenAndOddNumbers()) {
if (value > 10) break;
  console.log(value);
}
// Output: 2, 1, 4, 3, 6, 5, 8, 7, 10, 9

As we can see, the new Generator generates a sequence of both even and odd numbers interleaved with each other.

Handling Errors in Generators

Generators can also throw errors. We can use the throw() method to throw an error from a Generator. In the following example, we modify the evenNumbers() function to throw an error if the number generated is greater than 6.

function* evenNumbers() {
  let number = 0;
  while (true) {
    if (number > 6) throw new Error("Number greater than 6");
    number += 2;
    yield number;
  }
}

We can now call this function and catch the error using a try..catch block.

for (let value of evenNumbers()) {
  try {
    console.log(value);
  } catch (e) {
    console.error(e.message);
    break;
  }
}
// Output: 2, 4, 6, Error: Number greater than 6

As we can see, the error is caught using the try..catch block.

Benefits of Using Generators in Typescript Programming

Generators provide several benefits when used in TypeScript programming. One of the main benefits is the ability to generate complex sequences of values that can be generated over time. This feature is particularly useful in scenarios where we need to generate large sequences of values and we don’t want to generate all of them upfront.

Generators also provide lazy computation. We can delay the generation of values until they are needed. This can significantly reduce the amount of memory required for generating sequences of values.

Another benefit of Generators is their ability to pause and resume their execution. This feature opens up new possibilities for creating complex sequences of values that can be generated over time. The yield keyword is used in Generators to pause the execution and return a value to the caller. When the function is called again, it continues from the point where it left off.

Generators can also simplify the iteration over sequences of values. The for..of loop can be used to iterate over the sequence of values generated by a Generator function.

Conclusion

In conclusion, TypeScript Generators provide a powerful feature for generating sequences of values that can be iterated over using the for..of loop. Generators provide several benefits including the ability to generate complex sequences of values that can be generated over time, lazy computation, the ability to pause and resume their execution, and simplified iteration over sequences of values. With these benefits, Generators have become an important feature in TypeScript programming and are widely used in many applications.

Learning Typescript Iterators
Typescript

Learning Typescript Iterators

Introduction As a software developer who is always looking for efficient ways to write clean code, I discovered Typescript and the concept of iterators. Typescript is a JavaScript superset that enables the creation of more structured and maintainable code. In this article, I am introducing Typescript iterators and delving into their different types, how to […]

Mastering Typescript Enums
Typescript

Mastering Typescript Enums

As a web developer, I’m constantly learning new ways to improve my coding practices. One of the more recent additions to my toolkit is TypeScript, a powerful superset of JavaScript that provides additional features to make coding easier and more efficient. One of my favorite features of TypeScript is enums, which allow me to better […]

Learn Typescript Modules: Organization & Reusability
Typescript

Learn Typescript Modules: Organization & Reusability

As a developer, I am always looking for ways to make my code more organized and efficient. One tool that has helped me achieve this goal is Typescript modules. In this article, I will be discussing Typescript modules in-depth, including how to create and import them, how to declare dependencies, and the benefits of using […]