TypeScript - Type Guard

公開日:2019-02-11 更新日:2019-05-14

1. 概要

途中から変数の型を特定して、キャストせずに、個別のメソッドを実行できるようにします。

C#では、変数 obj が Car クラスだった場合、car と言う新しい変数を生成して、
if の中では変数 car を Carクラス として使用することができます。
if (obj is Car car) {
	car.drive();
}

TypeScript の場合、
typeof でもクラスの特定ができないため(object かどうかしかわからない)、
Type Guard と言う仕組みを使って、ソースの途中から、型を絞り込むことができます。
これにより、エディターによっては、インテリセンス(自動補完)による入力支援も使えます。

function 関数名(変数名: 型): 変数名 is 特定された型 {
	return true; //true の場合、引数の変数が、「特定された型」として扱われる
}

//呼び出し側
if (関数(変数)) {
	//この中では「特定された型」として使用できる
	変数.メソッド();
}

2.1 サンプル

class Dog {
	public name: string = "犬";
	public run(): string {
		return;
	}
}

function isDog(creature: any): creature is Dog {
	//typeof でクラスを判定できないため、メソッドの有無でクラスを判定します
	return (<Dog>creature).run !== undefined;
}

function getName(creature: any): void {
	if (isDog(creature)) {
		//このブロックの中では、creature は Dogクラスとして扱える
		console.log(creature.name);
	} else {
		console.log("未知の生物");
	}
}

getName({ a: 5 });  //未知の生物
getName(new Dog()); //犬

2.2 サンプル

//植物
class Plant {
	public seed; //種
}

//動物
class Animal { }

//犬
class Dog extends Animal {
	public run(): string {
		return "犬";
	}
}
//鳥
class Bird extends Animal {
	public fly(): string {
		return "鳥";
	}
}

function isDog(creature: Animal | Plant): creature is Dog {
	return (<Dog>creature).run !== undefined;
}
function isPlant(creature: Animal | Plant): creature is Plant {
	return (<Plant>creature).seed !== undefined;
}

function test(creature: any) {
	if (isDog(creature)) {
		let s = creature.run();
		console.log(s);
		return;
	}
	if (isPlant(creature)) {
		let s = creature.seed;
		console.log("植物:" + s);
		return;
	}
	
	console.log("該当なし");
}

test(new Dog);   //犬
test(new Bird);  //該当なし
test(new Plant); //該当なし

let plant = new Plant();
plant.seed = 5;
test(plant);     //植物:5

2.3 サンプル - Type Guard と instanceof の違い

instanceof では interface の判定が行えない点と、
変数の型が特定済みの場合は、instanceof で判定をしても、定義した時点の型として扱われます。

class Animal { v1: number; }
class Dog extends Animal { v2: number; }

function test(obj: any) {
	if (obj instanceof Animal) {
		console.log(obj.v1);
		
		//ビルドエラー
		//obj は Animal として扱われる
		//console.log(obj.v2);
	}
}

let obj = new Dog();
obj.v1 = 5;
obj.v2 = 10;
test(obj);

if (obj instanceof Animal) {
	console.log(obj.v1);
	
	//エラーにならない
	//obj は Dog のまま扱われる
	console.log(obj.v2);
}