3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeScript5.3で追加される(予定の)switch (true)による型の絞り込み

Posted at

はじめに

TypeScript5.3のBeta版の発表がありました。
そこでは様々な新機能の追加がありましたが、この記事ではswitch(true)における型の絞り込みについて紹介します。

型の絞り込み

TypeScriptでは制御フローに応じた型の絞り込みを行えます。string | number | undefinedを型として持つような変数が絞り込みが可能な制御フローのスコープ内ではstring | number | undefinedから特定のstringnumberundefinedを型として持つ変数として扱われます。

function narrowing(x: string | number | undefined) {
  if (typeof x === 'string') {
    // xの型はstringとして扱われる
    return x;
  }
  if (x !== undefined) {
    // xの型はnumberとして扱われる
    return x;
  }
  // xの型はundefinedとして扱われる
  return x;
}

switchによる制御フロー

TypeScript5.2以前ではswitch文による型の絞り込みは行えません。

function narrowing(x: string | number | undefined) {
  switch (true) {
    case (typeof x === 'string'):
      // xの型はstring | number | undefined
      x
      break
    case (x !== undefined):
      // xの型はstring | number | undefined
      x
      break
    default:
      // xの型はstring | number | undefined
      x    
  }
}

TypeScript5.3以降ではswitch文によって以下のように変数の方を絞り込むことが可能です(Playground)。

function narrowing(x: string | number | undefined) {
  switch (true) {
    case (typeof x === 'string'):
      // xの型はstring
      x
      break
    case (x !== undefined):
      // xの型はundefined
      x
      break
    default:
      // xの型はnumber
      x    
  }
}

ただ、switch文で内で文脈を読んで絞り込みが行われるようになったわけではないです。以下のような場合は絞り込みは行われないことに注意してください。

function narrowing(x: string | number | undefined) {
  switch (typeof x) {
    case ('string'):
      // xの型はstring | number | undefined
      x
      break
    case ('number'):
      // xの型はstring | number | undefined
      x
      break
    default:
      // xの型はstring | number | undefined
      x    
  }
}

function narrowing(x: string | number | undefined) {
  switch (false) {
    case (typeof x === 'string'):
      // xの型はstring | number | undefined
      x
      break
    case (typeof x === 'number'):
      // xの型はstring | number | undefined
      x
      break
    default:
      // xの型はstring | number | undefined
      x    
  }
}

つまり、switch (true)だった時にcaseの式に合わせてスコープ内で絞り込みが行われるということです。
なぜそうなっているか、どのように実装されているかはこちらのPRを参照してください。

おわりに

この記事ではTypeScript5.3の新機能として追加されるswitch(true)における型の絞り込みについて紹介しました。
以下のようにオブジェクトの一部によってユニオンを絞り込んで利用したいケースではif文の羅列よりもswitchで一連の分岐を表記すると見やすくなることもあるので選択肢の1つとして利用できるようになると良いと思いました。

type Result<T> = {
  success: true;
  contents: T
} | {
  success: false;
  contents: Error
}

function unwrap<T>(x: Result<T>) {
  switch (true) {
    case x.success:
      return x.contents;
    default:
      throw x.contents;
  }
}

type Loadable<T> = {
  state: "hasValue";
  contents: T;
} | {
  state: "loading";
  contents: Promise<T>;
} | {
  state: "hasError";
  contents: unknown;
};

async function getContents<T>(loadable: Loadable<T>) {
  switch (true) {
    case (loadable.state === 'hasValue'):
      return loadable.contents;
    case (loadable.state === 'loading'):
      return await loadable.contents;
    case (loadable.state === 'hasError'):
      throw loadable.contents;
  }
}
3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?