asによる型アサーションがコンパイラを騙して実行時エラーになる
asはコンパイラに「この値はこの型として扱ってよい」と伝える構文で、実際の値を変換したり検証したりはしません。unknown型の値をasで別の型に見せかけると、実際の値がその型のメソッドを持たない場合に実行時エラーになります。
エラーメッセージの読み方
main.ts:2
main.ts- ファイル名 — コンパイル前のTypeScriptソースです(sourceMapで変換後の位置を逆引きしています)
2- 行番号 — 実際にクラッシュした行
TypeError- 例外クラス — 何が起きたか。ここを検索するのが最短です
s.toUpperCase is not a function- 内容 — 期待していたもの、または受け付けられなかったもの
このエラーが出る典型パターン
パターン1
1 function shout(s: string): string { 2 return s.toUpperCase(); 3 } 4 const data: unknown = 42 ; ^ 5 console.log(shout(data as string));
main.ts:2
TypeError: s.toUpperCase is not a function
dataは実際にはnumberです。as stringはコンパイラを黙らせるだけで、実行時にはnumberにtoUpperCaseは存在せず例外になります。
直し方: 42 を "hi" にします。
広告
広告スロット(未設定)
パターン2
1 interface User { 2 name: string; 3 } 4 function greet(u: User): string { 5 return u.name.toUpperCase(); 6 } 7 const raw: unknown = 42 ; ^ 8 console.log(greet(raw as User));
main.ts:5
TypeError: Cannot read properties of undefined (reading 'toUpperCase')
rawは実際には数値です。as Userで型を偽装しても、実行時にはnameプロパティが存在せずundefinedになります。
直し方: 42 を { name: "Al" } にします。
パターン3
1 function sumAll(items: number[]): number { 2 return items.reduce((a, b) => a + b, 0); 3 } 4 const raw: unknown = "5" ; ^ 5 console.log(sumAll(raw as number[]));
main.ts:2
TypeError: items.reduce is not a function
rawは実際には文字列です。as number[]で型を偽装しても、文字列にreduceメソッドは存在しないため例外になります。
直し方: "5" を [1, 2, 3] にします。
よくある誤解
「asで型を指定したのだから、その型のはず」という思い込みは危険です。asは型チェックを通すための宣言に過ぎず、実際の値がその型の性質を満たしているかどうかは実行時まで分かりません。
まとめ
asによる型アサーションがコンパイラを騙して実行時エラーになるは中級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。