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チャンネルもご覧ください。