導入
Dog | Cat のように「どちらもオブジェクト」なユニオン型は、typeof では見分けがつきません(どちらも "object" になってしまいます)。そこで、プロパティの有無で判定する in 演算子や、自分で作る型ガード関数を使います。
説明
in 演算子は「そのプロパティを持っているか」で絞り込みます。
type Dog = { kind: "dog"; bark: () => void };
type Cat = { kind: "cat"; meow: () => void };
function speak(animal: Dog | Cat): void {
if ("bark" in animal) {
animal.bark(); // ここでは Dog に絞り込まれている
} else {
animal.meow(); // ここでは Cat に絞り込まれている
}
}
speak({ kind: "dog", bark: () => console.log("ワン!") });
speak({ kind: "cat", meow: () => console.log("ニャー!") });
もっと複雑な判定をまとめたいときは、戻り値の型を 引数 is 型 と書くユーザー定義の型ガードを作ります。
type Dog = { kind: "dog"; bark: () => void };
type Cat = { kind: "cat"; meow: () => void };
function isDog(animal: Dog | Cat): animal is Dog {
return animal.kind === "dog";
}
function speak(animal: Dog | Cat): void {
if (isDog(animal)) {
animal.bark();
} else {
animal.meow();
}
}
speak({ kind: "cat", meow: () => console.log("ニャー!") });
animal is Dog と書いておくと、isDog(animal) が true を返した分岐の中では、TypeScript が自動的に animal を Dog として扱ってくれます。
やってみよう
Dog と Cat に kind プロパティで分岐する switch 文を書いて、in 演算子を使わない方法も試してみましょう。
演習
Circle({ kind: "circle"; radius: number })と Square({ kind: "square"; side: number })という型を作ってください。area という関数を作り、引数 shape: Circle | Square を受け取って、in 演算子で判定し、Square なら side * side、Circle なら radius * radius(円周率は省いた簡易計算とします)を返すようにします。console.log(area({ kind: "square", side: 5 })) で結果を表示してください。
ヒント1を見る
if ("side" in shape) { return shape.side * shape.side; } のように書きます。
ヒント2を見る
else 側で return shape.radius * shape.radius; とします。