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)); ^
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[])); ^
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 )); ^
rawは実際にはbooleanです。as unknown as Shapeで型を偽装しても、実行時にはdescribeメソッドが存在せず例外になります。
直し方: raw as unknown as Shape を { describe: () => "a shape" } にします。
パターン4
1 interface Animal2 { 2 speak(): string; 3 } 4 function callSpeak(a: Animal2): string { 5 return a.speak(); 6 } 7 const raw: number = 7; 8 console.log(callSpeak(raw as unknown as Animal2)); ^
rawは実際にはnumberです。as unknown as Animal2は型チェックを完全に迂回するため、実行時にはspeakメソッドが存在せず例外になります。
直し方: raw as unknown as Animal2 を { speak: () => "woof" } にします。
パターン5
1 function joinAll(items: string[]): string { 2 return items.join(","); 3 } 4 const raw: boolean = true; 5 console.log(joinAll(raw as unknown as string[])); ^
rawは実際にはbooleanです。as unknown as string[]でコンパイラを欺いても、実行時にはjoinメソッドが存在せず例外になります。
直し方: raw as unknown as string[] を ["a", "b"] にします。
よくある誤解
「TypeScriptのasは無茶な変換を防いでくれるはず」という安心感は、unknownを経由した二重キャストには通用しません。unknownはあらゆる型からあらゆる型への変換を許すため、安全確認の意味を持たなくなります。
実務での勘所
unknown型がこの抜け道になる理由は、型の階層構造における特殊な位置づけにあります。あらゆる型の値はunknown型の変数に代入できる一方、unknown型の値を他の型としてasで扱うことも、型を絞り込む正当な操作として常に許可されます。この2つの性質を組み合わせると、値をいったんunknownという「なんでも受け入れる中継地点」に通すことで、本来なら直接は変換できない、まったく無関係な型同士の変換を実現できてしまいます。