はじめに
タイトルの通り、typeofなどの型ガードに続き、型述語(type predicates)について学んだのでまとめておく。
結論
以下のような引数の型がstringか否かを返す関数があった場合に、test is string
としている部分が型述語に当たる。
以下で、詳細をまとめる。
function isString(test: any): test is string{
return typeof test === "string";
}
詳細
型述語の仕様は以下の通りです。
// 関数の返り値がtrueの場合、testがstringであるとTypeScriptは推論する
function isString(test: any): test is string{
return typeof test === "string";
}
// また別のexample関数に渡されているfooという引数は、この時点ではany型である
function example(foo: any){
// isString関数の結果がtrueであった場合には、、
if(isString(foo)){
console.log("it is a string" + foo);
// string型の変数fooからlengthは呼び出し可能であるためコンパイルエラーにならない。
console.log(foo.length); // string function
}
}
// ランタイムでもエラーなく実行可能
example("hello world");
また、もしfoo
からtoExponential
というメソッドを呼び出した場合は以下の通りコンパイルエラーとなる。
※toExponential
は数値を指数表記で表した文字列を返すもので、string型には存在しない
function example(foo: any){
if(isString(foo)){
console.log("it is a string" + foo);
console.log(foo.length);
// toExponential はstring型には存在しない為コンパイルエラーとなる
console.log(foo.toExponential(2));
}
}
では、型述語(test is string)でfooがstring型に推論されているif文の外でtoExponential
メソッドを呼び出した場合は、コンパイルエラーにはならないが、実際に引数に渡ってきた値がstring型の場合にはtoExponential
メソッドが存在しないのでランタイムエラーとなる。
function example(foo: any){
if(isString(foo)){
console.log("it is a string" + foo);
console.log(foo.length);
}
// fooがstringだと推論されているif文の外なのでコンパイルエラーにはならない。
// が、実際に引数の型がstring型の場合にはtoExponentialメソッドが存在しないので、ここでランタイムエラーとなる。
console.log(foo.toExponential(2));
}
また、もし以下のようにtest is string
(型述語)を使用しない場合、
function isString(test: any): boolean{
return typeof test === "string";
}
function example(foo: any){
if(isString(foo)){
console.log("it is a string" + foo);
console.log(foo.length);
// fooがstringだと推論されているif文の中でtoExponentialメソッドを呼び出してもコンパイルエラーにはならない。
// が、やっぱりstring型の引数にはtoExponentialメソッドが存在しないので、ここでランタイムエラーとなる。
console.log(foo.toExponential(2));
}
}
おわりに
typeofよりも難しそうに思えたが、色々なケースを見て理解できた。これもエラーを事前に防ぐのに役立ちそうなので積極的に活用していきたい。