Tech

Tips

TypeScript

【ts-pattern】TypeScript にパターンマッチングを導入するメリット

2025年10月13日

2025年10月13日

【ts-pattern】TypeScript にパターンマッチングを導入するメリット

こんにちは、けいこんぐらです!

今回は ts-pattern というライブラリを紹介します。

ts-pattern は TypeScript でパターンマッチングを実現するためのライブラリで、複雑な条件分岐を簡潔に表現できる強力な手法です。

この手法を用いることで、より安全で読みやすいコードを書くための大きな助けとなります 👌

興味がある方はぜひ見てみてください!

ts-pattern とは?

改めてですが、公式でもこのように記載があります 👀

より適切で安全な条件式を記述しましょう。パターンマッチングにより、複雑な条件を単一の簡潔な式で表現できます。コードが短くなり、読みやすくなります。網羅性チェックにより、考えられるケースをすべて網羅的にチェックできます。

なかなか良さそうですね!

ちなみに、パターンマッチングというのは、関数型プログラミング言語でよく使われる手法で、データの構造に基づいて処理を分岐させる方法で、Python、Rust、Swift、Haskell などで広く使われています。

これを TypeScript で実現しようというのが ts-pattern です 🔎

パターンマッチングのメリット

まずは、従来の Switch 文での条件分岐を考えてみましょう。

ステータスを判断し、それに応じたメッセージを返す関数を考えます。

type FetchState = { status: "loading" } | { status: "success"; data: string } | { status: "error" };
 
function getState(fetchState: FetchState) {
  switch (fetchState.status) {
    case "loading":
      return "ローディング中...";
    case "success":
      return `${fetchState.data}`;
    case "error":
      return "エラーが発生しました";
  }
}

普通の Switch 文ですがなんだか見た目も冗長で、型安全性もあまり感じられない気がします 🤔

ここで試しに other という状態を追加してみましょう。

type FetchState =
  | { status: "loading" }
  | { status: "success"; data: string }
  | { status: "error" }
  | { status: "other" };
 
function getState(fetchState: FetchState) {
  switch (fetchState.status) {
    case "loading":
      return "ローディング中...";
    case "success":
      return `${fetchState.data}`;
    case "error":
      return "エラーが発生しました";
  }
}
 
// 'other' ケースが追加されたのに、Switch 文での網羅性チェックがされない、、

上記のように、other のケースが追加されたのに、Switch 文での網羅性チェックがされていません。

書き手のミスで、other のケースを忘れてしまう可能性があります 🙅‍♂️

一応、上記を回避する方法もあるにはあります!

type FetchState =
  | { status: "loading" }
  | { status: "success"; data: string }
  | { status: "error" }
  | { status: "other" };
 
function safeGuard(arg: never) {}
 
function getState(fetchState: FetchState) {
  switch (fetchState.status) {
    case "loading":
      return "ローディング中...";
    case "success":
      return `${fetchState.data}`;
    case "error":
      return "エラーが発生しました";
    default:
      safeGuard(fetchState);
    // Error:
    //   型 '{ status: "other"; }' の引数を型 'never' のパラメーターに割り当てることはできません。ts(2345)
  }
}
 
getState({ status: "other" });
// Output: undefined

しかし、上記のように冗長なコードを書く必要があり、結局、実行時にエラーになってくれません 💦

では、ts-pattern と使うとどうなるでしょうか?

import { match } from "ts-pattern";
 
type FetchState =
  | { status: "loading" }
  | { status: "success"; data: string }
  | { status: "error" }
  | { status: "other" };
 
function getState(fetchState: FetchState) {
  return match(fetchState)
    .with({ status: "loading" }, () => "ローディング中...")
    .with({ status: "success" }, (state) => `${state.data}`)
    .with({ status: "error" }, () => "エラーが発生しました")
    .exhaustive();
  // Error:
  //   この式は呼び出し可能ではありません。
  //   型 'NonExhaustiveError<{ status: "other"; }>' には呼び出しシグネチャがありません。ts(2349)
}
 
getState({ status: "other" });
// Output:
//   throw new NonExhaustiveError(input);
//   e [Error]: Pattern matching error: no pattern matches value "other"

上記のように、コンパイル時に網羅性チェックがされ、実行時にも例外が発生してくれます!

ここで変わったのは以下です、いい感じですね ✨

  1. 実行時の安全性が上がった
  2. 条件分岐が文から式になった
  3. 可読性が上がった

嬉しいポイント

ここでは個人的に嬉しいポイントを紹介します。

1. より複雑なケースにも対応できる

これは「2(形) × 2(色)」の簡単な例ですが、以下のように複数の条件を組み合わせたパターンマッチングも可能です。

Switch 文の場合は、ネストさせる必要があるので、可読性はかなり上がります 👏

import { match } from "ts-pattern";
 
type Shape = { type: "circle" } | { type: "square" };
type Color = "red" | "blue";
type ColoredShape = { shape: Shape; color: Color };
 
// 1. ts-pattern でパターンマッチングをする場合
const getColoredShape1 = (coloredShape: ColoredShape): string => {
  return match(coloredShape)
    .with({ shape: { type: "circle" }, color: "red" }, () => "Red Circle")
    .with({ shape: { type: "circle" }, color: "blue" }, () => "Blue Circle")
    .with({ shape: { type: "square" }, color: "red" }, () => "Red Square")
    .with({ shape: { type: "square" }, color: "blue" }, () => "Blue Square")
    .exhaustive();
};
 
// 2. switch文で同じことをする場合
const getColoredShape2 = (coloredShape: ColoredShape): string => {
  const { shape, color } = coloredShape;
  switch (shape.type) {
    case "circle":
      switch (color) {
        case "red":
          return "Red Circle";
        case "blue":
          return "Blue Circle";
      }
      break;
    case "square":
      switch (color) {
        case "red":
          return "Red Square";
        case "blue":
          return "Blue Square";
      }
  }
};

2. let を const にできる

ts-pattern の場合、式として評価されるので、let を使う必要がなくなり、const にできます。

これはめちゃくちゃシンプルな例ですが、以下のように書けます ✍️

import { match } from "ts-pattern";
 
const NUMBER = 5;
 
// 式なのでそのまま const で宣言できる
const isOdd = match((NUMBER % 2) as 0 | 1)
  .with(0, () => false)
  .with(1, () => true)
  .exhaustive();
 
// 文なので条件によって値を変更したい場合は、再代入するしかない
let isEven = false;
 
if (NUMBER % 2 === 0) {
  isEven = true;
}

まだまだ他にも、ts-pattern にはたくさんの機能が備わっているので、興味がある方はぜひドキュメントを覗いてみてください 💻

GitHub - gvergnaud/ts-pattern: 🎨 The exhaustive Pattern Matching library for TypeScript, with smart type inference.

🎨 The exhaustive Pattern Matching library for TypeScript, with smart type inference. - gvergnaud/ts

https://github.com

GitHub - gvergnaud/ts-pattern: 🎨 The exhaustive Pattern Matching library for TypeScript, with smart type inference.

デメリット

ts-pattern は内部的に型レベルの計算に依存しており、プロジェクトの型チェックが遅くなる可能性があるそうです 🐢

Switch 文を使っている方が、パフォーマンス的には良いかもしれません。

ただ、型安全性と保守性という面で考えると、個人的には ts-pattern を使う方が良いと思っていますが、ここはトレードオフの部分なので、プロジェクトの規模や要件に応じて選択するといいと思います!

まとめ

ts-pattern は TypeScript にパターンマッチングを導入するための強力なライブラリで、複雑な条件分岐を簡潔に表現でき、型安全性と可読性を向上させます ✅

個人的には、メリットとデメリットを考慮した上で、ts-pattern を積極的に採用していきたいと思っています。

何よりも、ネストが深くならずに済むケースが増え、可読性が上がるのが嬉しいです!

TypeScript の基本的な設計には組み込まれていないパターンマッチングなので、いろんな意見があるかと思いますが、興味がある方はぜひ試してみてください 👍

参考

Bringing Pattern Matching to TypeScript 🎨 Introducing TS-Pattern

In the past few years, frontend development has become increasingly declarative. React shifted our..

https://dev.to

Bringing Pattern Matching to TypeScript 🎨 Introducing TS-Pattern

前の記事

【資格】GitHub Foundations 認定 合格体験記

GitHub Foundations に合格した勉強法・学習期間・模試の使い方を実体験ベースでまとめた合格体験記です。

プロフィール

Profile Icon

けいこんぐら

ITエンジニア

大阪在住のサラリーマン。👨‍💻

新卒未経験でエンジニアとして働き、システムの開発等を行っている。

いつか、つよつよエンジニアになれるように日々頑張っています!

タグ