union型(string | number)のメソッド呼び出しで型エラーになる理由
string | numberのようなunion型の値に対して、片方の型にしか無いメソッド(toUpperCaseなど)を呼び出そうとすると、もう一方の型に無いことを理由にエラーになります。typeof等で型を絞り込む(narrowing)必要があります。
エラーメッセージの読み方
main.ts(2,16): error TS2339: Property 'toUpperCase' does not exist on type 'string | number'.
main.ts- ファイル名
2- 行番号
16- 列番号 — この位置でtscが検査に失敗しました
TS2339- エラーコード — 検索するとTypeScriptの解説が見つかります
Property 'toUpperCase' does not exist on type 'string | number'.- 内容 — 期待していたもの、または受け付けられなかったもの
このエラーが出る典型パターン
パターン1
1 function describe(value: string | number): string { 2 return value.toUpperCase(); ^ 3 } 4 console.log(describe("hi"));
valueはstring | number型です。toUpperCaseはstringにしかないメソッドなので、narrowingせずには呼べません。
直し方: toUpperCase を toString にします。
パターン2
1 function format(value: string | number): string { 2 return value.toFixed (2); ^ 3 } 4 console.log(format(3.14159));
toFixedはnumberにしかないメソッドです。string | number型のままでは呼び出せません。
直し方: toFixed を toString にします。
パターン3
1 function label(value: string | boolean): string { 2 return value.toUpperCase(); ^ 3 } 4 console.log(label(true));
toUpperCaseはbooleanには存在しません。string | boolean型のままでは呼び出せません。
直し方: toUpperCase を toString にします。
パターン4
1 function tag(value: string | number): string { 2 return value.toUpperCase(); ^ 3 } 4 console.log(tag("hi"));
valueはstring | number型です。toUpperCaseはstringにしかないメソッドなので呼べません。
直し方: toUpperCase を toString にします。
パターン5
1 function fmt(value: number | boolean): string { 2 return value.toFixed (2); ^ 3 } 4 console.log(fmt(3.14159));
toFixedはnumberにしかないメソッドです。number | boolean型のままでは呼び出せません。
直し方: toFixed を toString にします。
よくある誤解
「文字列を渡すつもりだから大丈夫」という呼び出し側の意図は、TypeScriptの型チェックには伝わりません。union型のまま扱う限り、tscは「numberが渡されるかもしれない」という前提でチェックします。
実務での勘所
typeofによる絞り込みが型として認識される仕組みを応用すると、union型に新しいメンバーを追加し忘れた場合の検出にも使えます。すべての分岐を書き終えた後のelseの中で、その変数をnever型の変数に代入するコードを書いておくと、将来union型に新しい型が追加されたのに対応する分岐を書き忘れた場合、絞り込みが完了せずnever型に代入できなくなり、コンパイルエラーとして知らせてくれます。この「neverへの代入」を使った網羅性チェックは、TypeScriptでよく使われる実務のテクニックです。