はじめに
前回の記事では、さまざまなTypeScriptの型の付け方・扱い方を解説しました。
第3回の今回は「TypeScriptで多様なデータに対応する方法」です。実際にありうるケースを考えながら、TypeScriptで型をあつかっていきます。
第1章 「型ガード」を理解して外部からのデータを扱おう
外部から読み込むデータの問題
TypeScriptで付けた型はコンパイルすると消えます。そのため実行時のコードでは型の確認は行えません。そこで困るのが外部のデータのあつかいです。
ファイル読み込みや通信で取得したデータは、JavaScriptのプログラムで受け取ります。TypeScriptの状態で受け取るわけではありません。そして、そのデータには型情報はなく、事前に静的解析を行えません。
こうした問題に対処する方法が、TypeScriptには用意されています。ここではそうした方法を見ていきます。
制御フロー分析と型ガード
TypeScriptは、変数や関数に付けた型を、代入するタイミングだけで検査しているわけではありません。より高度な処理を行っています。if文やfor文などの処理をたどり、変数に入っている型を絞り込んでいくことができます。
TypeScriptでは、実際にコードが実行された場合の制御フローを分析して、型の可能性を追跡します。こうした機能のことを制御フロー分析と呼びます。この制御フロー分析が、どのように行われるのかを確かめましょう。
まずは問題のあるコードの例を示します。
function getLen(p: string | undefined): number {
    let len: number = p.length;
    return len;
}
 引数pの文字列長を返す簡単なコードです。しかし、この処理には問題があります。p.lengthのpのところで、'p' is possibly 'undefined'.(「p」は「未定義」である可能性があります。)というエラーが起きます。
 引数pは、p: string | undefinedと型注釈が付いています。引数pは、undefinedの可能性があるわけです。
このコードを修正した例を示します。
function getLen(p: string | undefined): number {
    let len: number = 0;
    if (typeof p === "string") {
        len = p.length;
    }
    return len;
}
 if (typeof p === "string")のところで、変数pの型を判定しています。ここで型がstringであると絞り込んでいます。そのため、ブロック内のp.lengthはエラーになりません。
 この変数pの型の可能性が、どのように絞り込まれているのかを確かめるには、VSCode上で変数にマウスカーソルを重ねて、ポップアップを確認すると分かりやすいです。
 typeof pのところでは、変数pは(parameter) p: string | undefinedと表示されます。
 if文のブロック内のp.lengthのところでは、変数pは(parameter) p: stringと表示されます。型の可能性が絞り込まれているのが分かります。
こうした型のチェックを行うコードのことを、型ガードと呼びます。
さまざまな型ガード方法
型ガードはいくつかの方法で行えます。
 まずはtypeofです。前回登場したtypeofでは、注意すべき点があります。nullの判定です。JavaScriptでは、typeof nullは"object"を返します。そのため、オブジェクトとnullの可能性がある時は、さらに踏み込んだ条件分岐が必要になります。
まずは、型ガードがなにもない場合です。
function printKeys1(p: undefined | object | null): void {
    for (let v of Object.keys(p)) {
        console.log(v);
    }
}
printKeys1({name: "Tom", age: 10});
 Object.keys()の引数はオブジェクトでなければなりません。変数pは、undefinedの可能性もnullの可能性もありますのでエラーが起きます。
続いて、オブジェクトであるかを判定したケースです。
function printKeys2(p: undefined | object | null): void {
    if (typeof p === "object") {
        for (let v of Object.keys(p)) {
            console.log(v);
        }
    }
}
printKeys2({name: "Tom", age: 10});
 if (typeof p === "object")で型の可能性を絞り込みました。しかし、変数pは、nullの可能性が残っているのでエラーが起きます。
次のケースです。
function printKeys3(p: undefined | object | null): void {
    if (typeof p === "object" && p !== null) {
        for (let v of Object.keys(p)) {
            console.log(v);
        }
    }
}
printKeys3({name: "Tom", age: 10});
 これでようやくエラーが起きなくなります。&& p !== nullを追加することで、nullの可能性を排除したからです。
次の例です。
 型ガードには、特定のクラスのインスタンス化を判定するinstanceofも使えます。instanceofを使った型ガードの例を示します。
class Human {
    walk():void {}
}
class Bird {
    fly():void {}
}
function move(animal: Human | Bird) {
    if (animal instanceof Human) {
        animal.walk()
    }
    if (animal instanceof Bird) {
        animal.fly()
    }
}
 Humanのインスタンスの場合はwalkを使い、Birdのインスタンスの場合はflyを使っています。
 こうした判定は、「プロパティを持つか」を判定するin演算子でも行えます。例を示します。
function move2(animal: Human | Bird) {
    if ("walk" in animal) {
        animal.walk()
    }
    if ("fly" in animal) {
        animal.fly()
    }
}

 
              
               
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                          
                           
                              
                               
                              
                               
                              
                               
                              
                               
                              
                               
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
                      
                     
															
														 
															
														.png) 
     
     
     
     
     
													 
													 
													 
													 
													 
										
									
 
 
                    