TypeScriptにおけるジェネレーター関数

TypeScriptにおけるジェネレーター関数

この記事ではTypeScriptにおけるジェネレーター関数について説明します。

ジェネレーター関数の基本的な使い方から、非同期処理と組み合わせた応用例などをコードと共に学べます。

YouTube Video

ジェネレーター関数

TypeScriptにおけるジェネレーター関数は、JavaScriptのジェネレーター関数と同様の機能を提供します。ジェネレーター関数は、function*(アスタリスク付きの関数宣言)を使って定義され、通常の関数とは異なり、実行を一時停止・再開できる特殊な関数です。

ジェネレーター関数を呼び出すと、イテレーターが返され、このイテレーターを通じて値を1つずつ生成し、yieldキーワードを使って関数の実行を一時停止したり、外部から値を送り込んだりできます。

ジェネレーター関数の基本構文

 1function* myGenerator(): Generator<number, void, unknown> {
 2    yield 1;
 3    yield 2;
 4    yield 3;
 5}
 6
 7const gen = myGenerator();
 8
 9console.log(gen.next().value); // 1
10console.log(gen.next().value); // 2
11console.log(gen.next().value); // 3
12console.log(gen.next().done);  // true (Iteration finished)
  • function* myGenerator() でジェネレーター関数を定義します。
  • yieldキーワードは、値を返しながら関数の実行を一時停止します。
  • next()メソッドを呼び出すたびに、ジェネレーター関数の実行が再開され、次のyieldまで進みます。

next()は次の値とdoneプロパティを含むオブジェクトを返します。donetrueになったときは、すべての値が生成され、ジェネレーターの処理が終了したことを示します。

ジェネレーター関数の応用

ジェネレーター関数を使うと、シーケンシャルな処理を簡単に表現できます。次の例では、数列を生成するジェネレーター関数を作成します。

 1function* sequenceGenerator(start: number = 0, step: number = 1) {
 2    let current = start;
 3    while (true) {
 4        yield current;
 5        current += step;
 6    }
 7}
 8
 9const seq = sequenceGenerator(1, 2);
10
11console.log(seq.next().value); // 1
12console.log(seq.next().value); // 3
13console.log(seq.next().value); // 5
  • この例では、sequenceGeneratorは無限に増加する数列を生成します。yieldを使って、各ステップごとに値を返し、その後の呼び出しで次の値を生成します。

nextに値を渡す

next()メソッドは値を受け取ることができ、その値をジェネレーター関数の中に送り込むことができます。

 1function* adder() {
 2    const num1 = yield;
 3    const num2 = yield;
 4    yield num1 + num2;
 5}
 6
 7const addGen = adder();
 8addGen.next();          // Initialization
 9addGen.next(5);         // Set 5 to num1
10const result = addGen.next(10).value; // Set 10 to num2 and get result
11console.log(result);    // 15
  • この例では、next(5)next(10)によって、それぞれの値をジェネレーター関数内に送り込み、yield num1 + num2でその和を返します。

returnthrow

  • return(value)は、ジェネレーターを終了し、指定した値を返すことができます。
  • throw(error)は、ジェネレーター内部に例外を投げることができ、ジェネレーター内で例外処理を行う場合に使います。
 1function* testGenerator() {
 2    try {
 3        yield 1;
 4        yield 2;
 5    } catch (e) {
 6        console.error("Error caught:", e);
 7    }
 8}
 9
10const gen = testGenerator();
11console.log(gen.next().value); // 1
12gen.throw(new Error("An error occurred!")); // Error caught: An error occurred!
  • この例では、throwメソッドによってジェネレーター内にエラーを発生させ、そのエラーはジェネレーター内部でキャッチされます。

TypeScriptでの型定義

ジェネレーター関数の型定義は、次の形式で指定できます。

1// Generator<YieldType, ReturnType, NextType>
2function* myGenerator(): Generator<number, void, unknown> {
3    yield 1;
4    yield 2;
5    yield 3;
6}
  • Generator<YieldType, ReturnType, NextType>の形式で型を指定します。
    • YieldTypeは、yieldによって返される値の型です。
    • ReturnTypeは、returnで返される値の型です。
    • NextTypeは、next()に渡される値の型です。

次の例では、具体的な型を指定して、型安全にジェネレーターを使います。

 1function* numberGenerator(): Generator<number, void, number> {
 2    const num1 = yield 1;
 3    const num2 = yield num1 + 2;
 4    yield num2 + 3;
 5}
 6
 7const gen = numberGenerator();
 8
 9console.log(gen.next().value);   // 1
10console.log(gen.next(10).value); // 12 (10 + 2)
11console.log(gen.next(20).value); // 23 (20 + 3)

ジェネレーターと非同期処理

ジェネレーターは、非同期処理にも活用できます。たとえば、yieldを使って非同期処理の結果を待機しつつ、順次処理を進めることが可能です。ただし、TypeScriptやJavaScriptでは、async/awaitの方が一般的に使われることが多くなっています。

1function* asyncTask() {
2    const result1 = yield fetch("https://codesparklab.com/json/example1.json");
3    console.log(result1);
4
5    const result2 = yield fetch("https://codesparklab.com/json/example2.json");
6    console.log(result2);
7}

このように、非同期操作をジェネレーターで順番に処理することができますが、通常はPromiseやasync/awaitがより使いやすいため、非同期処理ではあまり使用されません。

まとめ

  • ジェネレーター関数は、function*で定義され、yieldで値を返しつつ、関数の実行を一時停止できる特殊な関数です。
  • **next()**を使ってジェネレーターを再開し、値を受け取ります。また、next(value)でジェネレーターに値を送り込むこともできます。
  • **return()throw()**を使って、ジェネレーター関数の終了やエラーハンドリングを行うことができます。
  • TypeScriptでジェネレーターを使う場合は、型定義を活用して型安全なコードを記述できます。

ジェネレーターは、イテレーションを柔軟にコントロールできる強力なツールです。

YouTubeチャンネルでは、Visual Studio Codeを用いて上記の記事を見ながら確認できます。 ぜひYouTubeチャンネルもご覧ください。

YouTube Video