Code Fix

初級

非nullアサーション(!)がコンパイラを騙して実行時エラーになる

変数名の後ろに!を付ける非nullアサーションは、「この値はnull/undefinedではない」とコンパイラに約束する構文です。実際にはundefinedだった場合、型チェックは通過しますが実行時に例外が発生します。

エラーメッセージの読み方

main.ts:2
main.ts
ファイル名 — コンパイル前のTypeScriptソースです(sourceMapで変換後の位置を逆引きしています)
2
行番号 — 実際にクラッシュした行
TypeError
例外クラス — 何が起きたか。ここを検索するのが最短です
Cannot read properties of undefined (reading 'toUpperCase')
内容 — 期待していたもの、または受け付けられなかったもの

このエラーが出る典型パターン

パターン1

 1  function shout(s: string): string {
 2    return s.toUpperCase();
 3  }
 4  const items: string[] = ["a", "b"];
 5  const target: string | undefined = items[5];
                                             ^
 6  console.log(shout(target!));
main.ts:2 TypeError: Cannot read properties of undefined (reading 'toUpperCase')

items[5]は範囲外なのでundefinedです。target!は「undefinedではない」とコンパイラに約束するだけで、実際の値までは変えません。呼び出し時に例外が起きます。

直し方: 50 にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  interface User {
 2    name: string;
 3  }
 4  function findUser(id: number): User | undefined {
 5    if (id === 1) return { name: "Al" };
 6    return undefined;
 7  }
 8  const u = findUser(2);
                       ^
 9  console.log(u!.name.toUpperCase());
main.ts:9 TypeError: Cannot read properties of undefined (reading 'toUpperCase')

id=2ではfindUserがundefinedを返します。u!はコンパイラの検査を止めるだけなので、実行時にはundefinedのままnameへアクセスして例外になります。

直し方: 21 にします。

この問題を解いてみる →

パターン3

 1  function toLength(items: number[] | undefined): number {
 2    return items!.length;
 3  }
 4  const data: number[] | undefined = undefined;
                                           ^
 5  console.log(toLength(data));
main.ts:2 TypeError: Cannot read properties of undefined (reading 'length')

dataがundefinedのままitems!でアクセスすると、実行時にlengthを読もうとして例外になります。

直し方: undefined[1, 2, 3] にします。

この問題を解いてみる →

よくある誤解

「型チェックを通過したのだから安全なはず」という思い込みは、!を使った箇所には当てはまりません。!はコンパイラの検査を黙らせるだけで、実行時の値そのものを保証するものではありません。

まとめ

非nullアサーション(!)がコンパイラを騙して実行時エラーになるは初級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。

演習をはじめる

関連するエラー

他の言語ではどうなるか

広告
広告スロット(未設定)