TypeScriptにおける型ガードまとめ

はじめに

TypeScriptで開発を行う上で型の判定を行う場合が多々あります。 自分自身たまにどうすればよいか迷子になってしまうこともあるため備忘録としてまとめておきます。

概要

  • プリミティブ型の判定:typeof
  • プロパティ・メソッドの存在確認による判定:in
  • クラスのインスタンスかどうかの確認による判定:instanceof
  • オブジェクトに判別可能なプロパティを設けてそれで判断する:Descriminated Unions

typeof

プリミティブ型(string, number, boolean, undefined, null, symbol, bigint)を文字列で返却してくれるため、それを同値演算子(===)もしくは非同値演算子(!==)を使って判定を行います。

// a,b どちらかがstringなら文字列結合、numberなら足し算を行う
const add = (a: string | number, b: string | number) => {
  if (typeof a === "string" || typeof b === "string") { // ここの部分
    return a.toString() + b.toString();
  }
  return a + b;
};

in 演算子

〇〇がオブジェクトに存在するか確認することで型ガードを行う方法です。

type Foo = {
  name: string;
  age: number;
}

type Bar = {
  name: string;
  email: string;
}

type FooBar = Foo & Bar;

const Baz: FooBar = (args: FooBar) => {
  console.log(args.name);
  if ("age" in args) { // argsにageプロパティがある場合
    console.log(args.age); 
  }
};

instanceof 演算子

指定したクラスのインスタンスであるかどうかを判定させ、型ガードを行う方法です。 interfaceで定義した型の情報はJavaScriptコンパイルされないため、この方法を使うことはできません。

class Foo {
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class Bar {
  constructor(name, email) {
    this.name = name;
    this.email = email;
  }
}
const Fuga = new Foo("foo", 25);
const Hoge = new Bar("bar", "test@example.com");

type Baz = Foo | Bar;

const whatIsInstance = (arg: Baz) => {
  console.log(arg.name);
  if (arg instanceof Foo) console.log(arg.age);
  if (arg instanceof Bar) console.log(arg.email);
};

Descriminated Unions

interface Bird {
  type: "Bird"; // ここが該当の箇所
  flyingSpeed: number;
}

interface Dog {
  type: "Dog"; // ここが該当の箇所
  runningSpeed: number;
]

type Animal = Bird | Dog;

const getSpeed = (animal: Animal) => {
  switch (animal.type) {
    case: "Bird":
      return animal.flyingSpeed;
    case: "Dog":
      return animal.runningSpeed;
  }
}