本文へスキップ
BecomeCoder

TypeScriptコース · 第4章 型を組み合わせる · レッスン13

型ガード ― in 演算子とユーザー定義の型ガード

ブラウザで完結

導入

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 が自動的に animalDog として扱ってくれます。

やってみよう

DogCatkind プロパティで分岐する switch 文を書いて、in 演算子を使わない方法も試してみましょう。

演習

Circle{ kind: "circle"; radius: number })と Square{ kind: "square"; side: number })という型を作ってください。area という関数を作り、引数 shape: Circle | Square を受け取って、in 演算子で判定し、Square なら side * sideCircle なら 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; とします。

実際に動かしてみよう

下のエディタにTypeScriptを書いて「実行」を押すと、その場でJavaScriptに変換してブラウザ内で実行し、出力が表示されます。本文の例をそのまま試したり、型注釈を書き換えたりしてみましょう(console.log の結果が下に出ます。初回だけコンパイラの読み込みに少し時間がかかります)。

TypeScript — ブラウザ内で実行

TypeScriptコンパイラ(同梱)でTS→JSに変換してから実行する学習用環境を読み込みます(初回のみコンパイラの読み込みに少し時間がかかります)。
スクロールして表示された時点でも自動で読み込まれます。