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プロパティを含むオブジェクトを返します。doneがtrueになったときは、すべての値が生成され、ジェネレーターの処理が終了したことを示します。
ジェネレーター関数の応用
ジェネレーター関数を使うと、シーケンシャルな処理を簡単に表現できます。次の例では、数列を生成するジェネレーター関数を作成します。
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でその和を返します。
return と throw
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チャンネルもご覧ください。