【TypeScript】any型とは。 少し勘違いしていました
更新日:2022/11/07
TypeScriptのany型は、なんでも代入できる型と理解していました。
any型を禁止するべきと言う人が多いですが、この理解だと大げさに聞こえます。
しかし、そうではなかったようです。
良く調べてみると、禁止したほうがいいと感じました。
そこで、もう一度確認の意味で any型についてまとめておきます
any型とは
any型は、型チェックをしない型です。
型チェックは引数としての受け渡しを含めた代入操作を行うときに行われます。
この時変数は、代入する側になることも、代入される側になることもあります。
変数aの代入:型チェックされない
a = 100; // 代入される側(左辺)
b = a; // 代入する側(右辺)
どちらの場合でも、両辺の型がチェックされて異なる時はエラーが出力されます。
しかし any型 がどちらかに配置されると、型チェックが行われません。
次の例は any型変数a に様々な型の値を代入し、最後に boolean型を代入しています。
次に、string型変数b に、変数a の値を代入しています。
any型の代入
let a; // aはany型
a = 100; // any型に number型を代入
a = "abc"; // any型に string型を代入
a = true; // any型に boolean型を代入
const b:string = a; // string型に boolean型を代入
最後の行を見てください。
変数a の値は true なので、boolean型です。
変数bは、b:string と記述されているので、string型です。
変数aとbの値は型が異なるので、代入できないはずです。
しかし変数aは値はboolean型ですが、変数の型がany型です。
そのため型チェックされず、結果的にエラー表示されません。
any型の型推論
型アノテーションで型付けされていない変数やプロパティ・引数は、ルールに従って型付けされます。
そして型を特定できない時、any型として型付けされます。
変数の型推論
変数は初期値の型で型付けされますが、初期値が与えられない時は any型です。
let a = 100; // 初期値あり。aは number型
let b; // 初期値なし。bは any型
引数の型推論
関数の引数も、型アノテーションがなければ any型です。
const func = function( a , b:string ){ // aはany型、bはstring型
return; // 戻り値は void型
}
戻り値がない場合、型が特定できないので any型のような気がしますが void型です。
any型との+演算
any型と +演算を行った結果は、基本的には any型です。
ただしstring型のみ、結果がstring型になります。
let a:any;
let b = a + "abc"; // b は string型
let c = a + 123; // c は any型
let d = a + false; // d は any型
let e = a + null; // e は any型
let f = a + undefined; // f は any型
JavaScriptの仕様では、文字列と+演算すると結果は全て文字列になります。
型が特定できるので、TypeScriptはstring型と判断しているのです。
配列の型推論
次のように空の配列を定義すると、要素は any型 になります。
const a=[];
a[0] = 1;
a[1] = "a";
const b = a[0]; // b は any型
const c = a[1]; // c は any型
定義時に要素を指定すると、値の型で型付けされます。
const a=["a",1,"b"];
a[0] = 1;
a[1] = "a";
const b = a[0]; // b は number型
const c = a[1]; // c は string型
問題点
any型の問題点は、変数に予期しない型がセットされる可能性があることです。
勘違いしてはいけないのは、any型に予期しない型がセットされるのは、予期しておくべきことです。
責任をもって、自分で対処する必要があります。
問題なのは、number型やstring型などの特定の型に、異なる型がセットされることです。
例えば、次のような関数があるとします。
const func = (a:string)=>{
return a.toUpperCase();
};
toUpperCase()はstring型の時有効なメソッドなので、他の型を引数で受け取ると実行時にエラーになります。
ですがTypeScriptが引数の型をチェックしてくれるので、他の型を気にする必要がありません。
しかし、次のように any型の値で呼び出した場合、値がnumber型だとしてもエラーになりません。
const a:any = 100;
console.log( func(a) );
しかし、TypeScriptが生成したコードを実行するとエラーになります。
TypeError: a.toUpperCase is not a function
TypeScriptはこのような問題がおこらないように型チェックしているのに、それが機能しない可能性があるのです。
any型を禁止する
any型は気軽に使うべきでは無い型です。
そもそも、本当にany型が必要なケースは、とても少ないです。
面倒だからという理由でany型を使用するのは避けた方がよさそうです。
方針としては、使用を禁止するくらいの気持ちでプログラミングするのが望ましいです。
その上で、noImplicitAnyオプションを適用すると、any問題のチェックが少しだけ改善します。
noImplicitAnyオプション
tscコマンドの noImplicitAnyオプションは、trueまたはfalseの真偽値で指定します。
規定値はfalseです。
「Implicit」は「暗黙」という意味なので、暗黙的なanyを否定するオプションです。
trueにすると、二つのチェックが有効になります。
暗黙的なany型引数チェック
関数の引数に型アノテーションが記述されていない時、エラーが出力されます。
これにより、暗黙的に any型が適用されるのを防ぐことができます。
暗黙的 any型でエラー
const func = (a)=>{
};
// エラー:Parameter 'a' implicitly has an 'any' type.
しかし、型アノテーションでany型指定したときは、エラーになりません。
明示的なany型はエラーではない
const func = (a:any)=>{
};
ようするに、引数の型アノテーション記述忘れ防止オプションですね。
any型禁止オプションではないようです。
暗黙的なany型変数の型追跡
暗黙的なany型変数は、代入する度に値の型で型付けされます。
let a; // a は any型
a = "abc"; // a は string型
a = 100; // a は number型
そのため、次のように引数の型チェックが有効になります。
const func = (a:string)=>{
};
let a; // a は any型
a = 100; // a は number型
func(a); // エラー: Argument of type 'number' is not assignable to parameter of type 'string'.
同じように配列についても、追跡されます。
let a=[];
a[0] = 100; // a[0]は number型
func(a[0]); // エラー: Argument of type 'number' is not assignable to parameter of type 'string'.
どちらも便利そうな機能ですが、型アノテーションされると追跡されません。
const func = (a:string)=>{
};
let a:any; // a は any型
a = 100; // a は any型
func(a); // エラーにならない
最後に
とりあえず、any型変数を他の型に代入できなくオプションが欲しいです。
更新日:2022/11/07
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。