はじめに
TypeScriptの型定義について、改めて学んでいく中で
「プロを目指す人のためのTypeScript」(通称:ブルーベリー本)の中で、
any型が 「最終兵器」「最凶の危険性を誇る」 と書かれており
改めて危険性について備忘も兼ねて記載しておきます。
そもそも:TypeScriptの特徴「静的型付け」について
危険性の話に入る前にTypeScriptの特徴である「静的型付け」について。
TypeScriptは静的型付けの言語です。
静的型付けの対義語として動的型付けという言葉が挙げられます。
大きな違いはいつ型が決まるのかです。
- 静的型付け:コンパイル時に型が決まる
- 動的型付け:ランタイム時に型が決まる
静的型付け言語は、
型についてのエラーはランタイム時より前のコンパイル時に気づくことができるというメリットがあります。
サバイバルTypeScriptの中では型注釈について
型注釈は、あなた専属のコードレビュアであるコンパイラを育てるための投資。
と記載がありました。個人的にしっくりくる表現です。
TypeScriptを使うということは、静的型付け言語のメリットを享受したいという
前提があって使っているはず、その上でanyのお話です。
any型=どんな型でもOK ではない
any型は定義した値や式がどんな型でもOKな型ではなく、
型チェックを無効化する型という認識の方が危険性の理解が深まりました。
ここで型チェックが行われないことによってどのような事象が起こるか
2つのケースを記載します。
①コンパイルエラーにならずランタイムエラーになるケース
例えば、次のような関数があるとします。この関数はコンパイルエラーにはなりません。
function doAnything(obj: any) {
//好きなプロパティにアクセスできる
console.log(obj.users.name);
//関数呼び出しも可能
obj();
//計算もできる
const result = obj * 10;
return result;
}
プロパティにアクセスができ、関数呼び出しもでき、計算もできる、
一見便利に見えるかもしれないですが、例えば以下の呼び出しは
コンパイルエラーは発生せずランタイム時にエラーになります。
doAnything(100);
doAnything(() => {
console.log("OK");
});
②コンパイルエラー、ランタイムエラーにならず想定した結果にならないケース
例えば、引数のa,bを足し算して返すという以下のような簡単な関数があった場合
function addNumbers(a: any, b: any): any {
return a + b;
}
ここでresultは数字と文字列を渡した場合、コンパイルエラーにはなりません。
const result = addNumbers(5, "10"); // エラーにならない
console.log(result); // 出力結果は "510"
①②どちらも静的型付け言語の
型についてのエラーはランタイム時より前のコンパイル時に気づくことができるというメリットが
享受できない状態になっているというわけです。
最後に
any型はメリットが享受できない=TypeScriptを使う意味合いがなくなる という
理解の方が良さそうかなと。
(逆にanyを使えば、メリットは享受できないですが、
コンパイルエラーを発生させずに実装できるとも言えなくもないかなと。。)
any=ダメ、絶対。というよりは
明確な理由を持って使う(=明確な理由がない場合は避ける)、
明確な理由(どういうケースだとanyを許容するのか)をチーム内で明確にしておきたいなと思いました。
参考文献