Menu

TypeScriptのループ: for文、forEach、for...of、for...in

TypeScriptのループの書き方と、それぞれで得られる型。基本の for 文、配列・Map・文字列に対する for...of、文字列のキーを返す for...in、break も await も使えない forEach、オブジェクトのキーを型付きでループする方法、while を解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

TypeScriptにはJavaScriptと同じループがあります: for、for...of、for...in、while、do...while、そして配列のメソッド forEach です。TypeScriptが加えるのは型です。for...of のループ変数には要素の型が付き、for...in のキーは常に string です。

配列のループのほとんどは for...of で書くのがおすすめです。読みやすく、break、continue、await が使え、型注釈も要りません。

基本の for 文

for (init; condition; update) は、数を数える、2つ以上ずつ進める、逆順にたどるといったときのループです。TypeScriptはカウンターの型を初期値から推論します。

カウンターは var ではなく let で宣言します。let なら反復ごとに別の束縛が作られるので、ループの中で作ったコールバックはその反復の値を参照します。

for...of: 配列、文字列、Map の値

for...of は反復可能なものなら何にでも使え、ループ変数には対応する型が付きます。インデックスも必要なら、[index, value] のタプルを返す array.entries() をループします。

break はループを抜け、continue は次の要素に進みます。for 文とまったく同じです。実行時の詳細(反復可能オブジェクト、ジェネレーター)はJavaScriptの for...of と for...in のページで説明しています。

forEach: break も await も使えない

forEach は要素ごとに関数を呼び、undefined を返します。コールバックのパラメーターは配列から型が付くので、型注釈は要りません。できないことが2つあります:

  • 途中で止められない。 コールバックの中の break はコンパイルエラー TS1107 Jump target cannot cross function boundary. です。return は現在の呼び出しを終えるだけなので、continue のように動きます。
  • 非同期の処理を待てない。 forEach は async コールバックが返す Promise を無視します。

非同期の落とし穴は警告なしにコンパイルが通るので、見落としがちです:

forEach のコールバックは、ループが先に進んだあとでタイマーの順(10、20、30)に終わります。for...of 版は順番に実行します。並列に実行しつつ完了を待ちたいなら、await Promise.all(items.map(async (item) => { ... })) を使います。

for...in: キーは文字列

for...in はオブジェクトの列挙可能なプロパティ名をループします。キーの型は常に string で、配列でもインデックスが文字列になります。そのため、型付きのオブジェクトをインデックスで参照するには不便です:

index.ts(5,22): error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ name: string; age: number; }'.
  No index signature with a parameter of type 'string' was found on type '{ name: string; age: number; }'.

TypeScriptはわざとキーを string と型付けしています。実行時のオブジェクトは型に書かれた以上のプロパティを持てます(構造的型付けでは余分なプロパティが許されます)し、for...in は継承した列挙可能なプロパティもたどります。そのため、キーが "name" | "age" だと保証できないのです。直し方は次のセクションにあります。配列に for...in を使うのはやめましょう。

オブジェクトのキーと値をループする

オブジェクトをループするときは、[key, value] のペアを返す Object.entries を使うのが一般的です。Object.keys は for...in と同じ理由で string[] を返すので、オブジェクトに余分なキーがないとわかっている場合はキーの型をアサーションで指定します。

キーが事前にわからないデータには、オブジェクトを Record<string, number> として型付けするか Map を使います。どちらも辞書のページで説明しています。

while と do...while

while は毎回の実行の前に条件をチェックし、do...while は本体を一度実行してからチェックします。コレクションをたどらないループ、たとえば条件が満たされるまで読み続ける処理に向いています。この中でも型の絞り込みは効きます。条件で !== undefined をチェックすれば、本体の中では変数が絞り込まれます。

入れ子のループでのラベル、break、continue

普通の break はいちばん内側のループしか抜けません。外側のループを抜けるには、ループにラベルを付けてそのラベルを指定します:

どのループを使うか

やりたいことループ
配列のすべての要素for (const x of arr)
要素とインデックスfor (const [i, x] of arr.entries()) または arr.forEach((x, i) => ...)
途中で止めるbreak を使う for...of、または find、some、every
各ステップを await する中で await する for...of
新しい配列を作るmap、filter(ループではない)
数を数える、n ずつ進めるfor (let i = 0; ...; i += n)
オブジェクトのキーと値for (const [k, v] of Object.entries(obj))
Map のエントリーfor (const [k, v] of map)
条件を満たすまでwhile

よくある質問

TypeScriptで for 文を書くには?

JavaScriptと同じです: for (let i = 0; i < 5; i++) { ... }。TypeScriptは i を number と推論します。配列の要素を順に処理するなら for (const item of items) のほうが短く、item には配列の要素の型が付きます。

TypeScriptで forEach を途中で抜けるには?

抜けられません。コールバックの中の break はコンパイルエラー TS1107(Jump target cannot cross function boundary)になり、return は continue のように現在のコールバックの呼び出しを終えるだけです。break と組み合わせた for...of か、もともと途中で止まる some、find、every を使います。

TypeScriptの for...of と for...in の違いは何ですか?

for...of は反復可能なもの(配列の要素、文字列の文字、Map のエントリー)の値を、本来の型のまま順に処理します。for...in はオブジェクトの列挙可能なプロパティのキーを処理し、その型は常に string で、配列のインデックスも文字列になります。配列には for...of を、オブジェクトには Object.entries を使いましょう。

TypeScriptでオブジェクトのキーと値をループするには?

for (const [key, value] of Object.entries(obj)) を使います。key は string、value はプロパティの型のユニオンです。key を keyof typeof obj として型付けしたい場合は、(Object.keys(obj) as (keyof typeof obj)[]) のようにアサーションを使います。TypeScriptは意図的に Object.keys を string[] と型付けしているからです。

forEach の中で await を使えますか?

コンパイルは通りますが、forEach はコールバックが返す Promise を待たないので、処理が終わる前にループが終わってしまいます。順番に処理するなら中で await する for...of を、並列に処理するなら await Promise.all(items.map(async (x) => ...)) を使います。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める