配列の範囲外アクセスがエラーにならずundefinedになる(TypeScriptでも変わらない)
TypeScriptは配列の要素数を型では追跡しないため、number[]のような型でも範囲外アクセスはコンパイルエラーになりません。実行時もJavaScriptと同じく、静かにundefinedを返します。
なぜエラーが出ないのか
出力: undefined
(エラーなし)- TypeScriptは何も報告しません。型検査も構文も正しいためです
出力: undefined- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 const nums: number[] = [10, 20, 30]; 2 console.log(nums[5]); ^
nums[5]は範囲外です。TypeScriptもJavaScriptと同じくエラーにはならず、静かにundefinedを返します。
直し方: 5 を 1 にします。
パターン2
1 const colors: string[] = ["red", "green", "blue"]; 2 console.log(colors[10]); ^
配列の長さは3なのでインデックス10は存在しません。例外にはならず、undefinedが出力されます。
直し方: 10 を 2 にします。
パターン3
1 const scores: number[] = [80, 90]; 2 console.log(scores[-1]); ^
TypeScriptの配列は負のインデックスを末尾からの参照として扱いません。範囲外と同じくundefinedになります。
直し方: -1 を 0 にします。
パターン4
1 const vals: number[] = [7, 8, 9]; 2 console.log(vals[9]); ^
vals[9]は範囲外です。例外にはならず、静かにundefinedを返します。
直し方: 9 を 1 にします。
パターン5
1 const words: string[] = ["a", "b"]; 2 console.log(words[-3]); ^
負のインデックスは末尾からの参照にはならず、範囲外と同じくundefinedになります。
直し方: -3 を 0 にします。
よくある誤解
「型を付けたのだから範囲外アクセスも検出してくれるはず」という期待は誤りです。TypeScriptの配列型は要素の型だけを表し、長さや添字の妥当性までは追跡しません。
実務での勘所
この問題に対処するtsconfig.jsonの設定として、noUncheckedIndexedAccessというオプションがあります(strict: trueに自動では含まれない、追加で有効にする必要がある設定です)。これを有効にすると、number[]型の配列に対するarr[i]の型がnumberではなくnumber | undefinedになり、範囲外だった場合の可能性を型として強制的に意識させられます。既定のstrictモードだけでは防げないこの種のバグに対しては、この設定を追加で有効にすることが推奨されています。
関連するエラー
- 非nullアサーション(!)がコンパイラを騙して実行時エラーになる変数名の後ろに!を付ける非nullアサーションは、「この値はnull/undefinedではない」とコンパイラに約束する構文です。
- 文字列と数値の+の左結合で意図しない連結になる文字列と数値を+で連続してつなげると、最初の+で文字列に変換され、それ以降の+はすべて文字列連結として扱われます。