構文

【TypeScript】is演算子って何?ユーザー定義の型ガード

更新日:2022/11/07

TypeScriptには is演算子というものがあります。
どうやら型ガードに関する演算子のようです。
知っていると意外と使用する機会が多いです。

そこで今回は、TypeScriptには is演算子についてお伝えします。

 

is演算子とは

TypeScriptのis演算子を関数の戻り値の型に使用すると、型ガード関数を定義できます。

const isString = (value:any):value is string=>typeof value === "string";

is演算子は、引数 is 型という形式で指定します。
この形式は、型述語(type predicate)と呼ばれています。

型述語が定義されている関数は真偽値を返します。
そして true を返す時は、引数の型が特定の型であることを指示します。

この特性により、typeof 変数 === "string" などの型ガードと同等の関数を定義できます。

型ガードについては次の記事でも紹介しているので、参考にしてみてください。

 

使用例

ユーザー定義の型ガードの使用例をいくつか挙げます。

プリミティブ型のユーザー定義型ガード

次のコードはstring型またはnumber型を引数として受け取る関数内で、引数がstring型かどうかをユーザー定義の型ガードを使って判定しています。

const isString = (value:any):value is string=>typeof value === "string";

const func = (value:string|number):string=>{
    if( isString(value) ){
         // valueはstring型
        return value.toUpperCase();
    }else{
        // valueはnumber型
        return value.toString();
    }
}

if( isString(value) ){ } { }内では、isString()の効果で value に string型が型付けされます。
一方 else{ }側は、string型ではないことが考慮されて value に number型が型付けされます。

この例は、func関数のif文に直接 typeof value === "string" を記述できます。
同じ判定を複数個所で行っていないなら、ユーザ定義関数を使う意味は薄いかもしれません。

クラス型のユーザー定義型ガード

次のコードは、二つのクラスを判別しています。

class Person1{
    name:string;
    constructor(name: string) {
        this.name = name
      }
}
class Person2{
    name:string;
    familyName:string;
    constructor(name: string,familyName:string) {
        this.name = name;
        this.familyName = familyName;
      }
}
const isPerson2 = (person:Person1| Person2):person is Person2=>person instanceof Person2;

const func = (param:Person1| Person2):string=>{
    if( isPerson2(param)  ){
        return param.name;
    }else{
        return param.name + " " + param.familyName;
    }
}

この例は、func関数のif文に直接 person instanceof Person2 を記述できます。
最初の例と同様に、ユーザ定義関数を使う意味は薄いかもしれません。

オブジェクト型のユーザー定義型ガード

次のコードは二つのオブジェクト型を、判別しています。

type Person1 ={
    name:string
}
type Person2 ={
    name:string,
    familyName:string
}
const isPerson2 = (person:Person1| Person2):person is Person2=>"familyName" in person;

const func = (param:Person1| Person2):string=>{
    if( isPerson2(param)  ){
        return param.name+ " " + param.familyName;
    }else{
        return param.name ;
    }
}

type Person1 や type Person2 は実体を生成しないので、instanceofの対象にできません。
そのため、プロパティの有無で判別しています。

この例は typeof や instanceofが使用できないパターンです。
ユーザー定義の型ガードが最も有効なパターンですね。

配列型と他の型のユーザー定義型ガード

よくあるパターンに、配列またはプリミティブや関数など他の型を引数として受け付ける関数があります。

次のコードは数値または数値を要素に持つ配列を引数として受け取って、数値ならそのまま、配列なら合計します。
そして文字列に変換して返す関数を定義しています。

const func = (value:number|number[]):string=>{
    if( Array.isArray(value) ){
           // valueはnumber[]型
        return value.reduce((a,b)=>a+b).toString();
    }else{
           // valueはnumber
        return value.toString();
    }
}
console.log( func([1,2,3]));  // 結果:6
console.log( func(5));         // 結果:5

型ガード関数を定義せずに、Array.isArray() を使用しています。
このメソッドは、lib.es5.d.tsファイルで次のように定義されています。

ArrayConstructor.isArray(arg: any): arg is any[]

戻り値の型が型述語なので、Array.isArray() は型ガードです。
型がany[]ですが valueの型が number|number[] なので、number[] が選択されています。

更新日:2022/11/07

書いた人(管理人):けーちゃん

スポンサーリンク

記事の内容について

null

こんにちはけーちゃんです。
説明するのって難しいですね。

「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。

裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。

掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。

ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php

 

このサイトは、リンクフリーです。大歓迎です。