as unknown as Xの二重キャストは型チェックを完全に迂回し、実行時エラーの原因になる
TypeScriptは通常、明らかに無関係な型同士のasによるキャストをエラーにします。しかしunknownを経由する(as unknown as X)と、この安全装置を完全に迂回でき、どんな型にも変換できてしまいます。
エラーメッセージの読み方
main.ts:5
main.ts- ファイル名 — コンパイル前のTypeScriptソースです(sourceMapで変換後の位置を逆引きしています)
5- 行番号 — 実際にクラッシュした行
TypeError- 例外クラス — 何が起きたか。ここを検索するのが最短です
Cannot read properties of undefined (reading 'toUpperCase')- 内容 — 期待していたもの、または受け付けられなかったもの
このエラーが出る典型パターン
パターン1
1 interface User { 2 name: string; 3 } 4 function greet(u: User): string { 5 return "Hello, " + u.name.toUpperCase(); 6 } 7 const raw: number = 42; 8 console.log(greet(raw as unknown as User)); ^
main.ts:5
TypeError: Cannot read properties of undefined (reading 'toUpperCase')
rawは実際にはnumberです。as unknown as Userは型チェックを完全に迂回するため、実行時にはnameプロパティが存在せずundefinedになります。
直し方: raw as unknown as User を { name: "Al" } にします。
広告
広告スロット(未設定)
パターン2
1 function sumAll(items: number[]): number { 2 return items.reduce((a, b) => a + b, 0); 3 } 4 const raw: string = "5"; 5 console.log(sumAll(raw as unknown as number[])); ^
main.ts:2
TypeError: items.reduce is not a function
rawは実際には文字列です。as unknown as number[]でコンパイラを欺いても、文字列にreduceメソッドは無いため実行時に例外になります。
直し方: raw as unknown as number[] を [5] にします。
パターン3
1 interface Shape { 2 describe(): string; 3 } 4 function show(s: Shape): string { 5 return s.describe(); 6 } 7 const raw: boolean = true; 8 console.log(show(raw as unknown as Shape )); ^
main.ts:5
TypeError: s.describe is not a function
rawは実際にはbooleanです。as unknown as Shapeで型を偽装しても、実行時にはdescribeメソッドが存在せず例外になります。
直し方: raw as unknown as Shape を { describe: () => "a shape" } にします。
よくある誤解
「TypeScriptのasは無茶な変換を防いでくれるはず」という安心感は、unknownを経由した二重キャストには通用しません。unknownはあらゆる型からあらゆる型への変換を許すため、安全確認の意味を持たなくなります。
まとめ
as unknown as Xの二重キャストは型チェックを完全に迂回し、実行時エラーの原因になるは上級でつまずきやすい項目です。上の3パターンを実際に手で直すと、エラーメッセージのどこを読めばよいかが掴めます。