https://qiita.com/ryo2132/items/ce9e13899e45dcfaff9b

TypeScript の構文の中でも、屈指のググラビリティの低さを誇る isin を自分なりにまとめてみました。

"is"

is は TypeScript の型推論を補強する user-defined type guard(ユーザー定義型ガード) で使われます。 unknown 型や、any 型、Union 型の型の絞り込みを行えます。

使用例

例えば、unknown 型を引数に受け取る関数で、もし引数の型が string 型だったら文字列の長さを出力するという処理があるとします。 その場合は、以下のように型を絞り込む必要があります。

const example = (foo: unknown) => {
  if (typeof foo === "string") {
    console.log(foo.length); // fooはstringとして推論される
  }
};

この一つの関数ならば良いのですが、他にも string 型への絞り込みが必要な関数があり、typeof foo === "string" の部分を isString 関数に抽出して汎用的に使いまわせるようにしたいとします。

一見上手くいきそうですが、typeof での型の絞り込みは関数スコープで完結してしまうので、isString が true な場合でも、まだ foo は unknown 型として推論され型の絞り込みが行えません。

const isString = (test: unknown): boolean => {
  return typeof test === "string";
};

const example = (foo: unknown) => {
  if (isString(foo)) {
    console.log(foo.length); // Error fooはまだunknownとして推論される
  }
};

このような時が is の出番です。is を使うと isString の結果が true の場合は引数で受け取った変数の型は、string 型であるとコンパイラに教えることができます。

const isString = (test: unknown): test is string => {
  return typeof test === "string";
};

const example = (foo: unknown) => {
  if (isString(foo)) {
    console.log(foo.length); // fooはstringとして推論される
  }
};

また、is はオブジェクト型の型の絞り込みにも使えます。 例えば Fish か Bard の Union 型の型をどちらかの型に絞り込みたい時はif((fishOrBard as Bard).fly() !== undefined){}で判定を行えそうです。

しかし、この判定ではまだ TypeScript のコンパイラは fishOrBard を Bard 型と推論してはくれません。

type Bard = {
  fly: () => {
    /*  Do something */
  };
};

type Fish = {
  swim: () => {
    /*  Do something */
  };
};

const example = (fishOrBard: Fish | Bard) => {
  if ((fishOrBard as Bard).fly() !== undefined) {
    console.log(fishOrBard.fly); // Fish | Bard 型として推論され Fishはfly()を持たないので type error
  } else {
    console.log(fishOrBard.swim); // Fish | Bard 型として推論され Bardはswim()を持たないので type error
  }
};

そのような時に (fishOrBard as Bard).fly() !== undefined を関数に切り出し、is で型を指定するとコンパイラは型を推論できるようになります。

const isBard = (test: Fish | Bard): test is Bard => {
  return (test as Bard).fly() !== undefined;
};

const example = (fishOrBard: Fish | Bard) => {
  if (isBard(fishOrBard)) {
    console.log(fishOrBard.fly); // Bard型として推論される
  } else {
    console.log(fishOrBard.swim); // Fish型として推論される
  }
};

is 自体への型推論は効かないので注意です。間違った指定をしてしまうとコンパイルは通っても実行時エラーになります。

const isString = (test: unknown): test is string => {
  return typeof test === "number";
};

const example = (foo: unknown) => {
  if (isString(foo)) {
    console.log(foo.length); // 型エラーは出ないが、fooはnumber型なので実行時Errorが発生する
  }
};