TypeScript の Template Literal Types でAPI のイベント名を型安全にした話

WebSocket のイベント名や API パスを string 型で渡していたコードに Template Literal Types を導入したところ、補完が効くようになりタイポによるバグがかなり減りました。

🙌 結論から

TypeScript の Template Literal Types を使うと、string 型として素通しにしていた文字列を、特定のパターンに絞った型として扱えるようになります!

WebSocket のイベント名や API のエンドポイントパスをまるごと string で渡していたコードに導入したところ、補完が効くようになり、タイポが原因のバグがかなり減りました(^o^)/

使い方はそれほど難しくないのに、知っている人と知らない人で型の表現力がだいぶ変わります。

💡 Template Literal Types とは

TypeScript 4.1 から使えるようになった機能で、テンプレートリテラルの構文を型の世界に持ち込んだものです。

たとえば、こんなふうに書けます。

type EventName = `on${Capitalize<string>}`;
// "onClick" | "onChange" | "onFocus" など "on〇〇" の形を表す型

これを使うと "onClick" は OK、"click" は NG という制約を型で表現できます!

より実用的な例だと、ユニオン型との組み合わせがよく使われます。

type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = `/api/${string}`;

type ApiConfig = {
  method: HttpMethod;
  url: Endpoint;
};

こうすると url"https://example.com" を渡した瞬間に型エラーになり、うっかり外部 URL を渡すミスを防げます。

👀 実際に使った例

私が最初に導入したのは、WebSocket のイベント名の管理でした。

以前は socket.on('user:join', handler) のようにイベント名を直接文字列で書いていて、'user:jion' というタイポが発生しても実行時までわかりませんでした(´・ω・`)

そこで以下のように型を定義しました。

type Namespace = 'user' | 'room' | 'message';
type EventVerb = 'join' | 'leave' | 'send' | 'receive';
type SocketEvent = `${Namespace}:${EventVerb}`;

// 有効: "user:join", "room:leave", "message:send" ...
// 無効: "user:jion" → コンパイルエラーになる!

これだけで SocketEvent 型には Namespace × EventVerb の組み合わせしか入れられなくなります。

エディタの補完もきちんと機能して、タイポの心配がなくなったのがかなりありがたかったです!

🤔 ハマりポイント

組み合わせ数が爆発的に増える点だけ注意が必要です(・_・;)

type A = 'a1' | 'a2' | 'a3';
type B = 'b1' | 'b2' | 'b3';
type C = 'c1' | 'c2' | 'c3';
type Combined = `${A}-${B}-${C}`; // 3×3×3 = 27通り

ユニオン型の組み合わせが増えると TypeScript の型チェックが重くなることがあります。

10〜20 パターン程度であれば問題ないケースがほとんどです。

ドメインが広い場合は string を使いつつコメントでパターンを示す、というバランスが現実的かもしれません。

もう一点、Capitalize<T> などのユーティリティ型を活用すると定義が簡潔になりますが、IDE の型ヒントが少し読みづらくなることがあります。

実際に使いながらトレードオフを確認していくのがいいと思います!

パパ系Webエンジニア〜愛用ガジェット一覧 🛍️

この記事を書いている作業環境のガジェットを楽天ROOMにまとめています ✨ 在宅エンジニアのデスク作りの参考にどうぞ 🙌

🙌 まとめ

  • Template Literal Types は TypeScript 4.1 以降で使える
  • ユニオン型と組み合わせると、文字列の「パターン」を型で制約できる
  • イベント名・API エンドポイント・CSS クラス名など、規則性のある文字列に相性がいい
  • 組み合わせ数が多くなりすぎると型チェックが重くなる点に注意

string で素通しにしている箇所が多いと感じているなら、まず 1〜2 箇所だけ試してみるのがおすすめです(^^)

比較的小さい変更なのに、補完とエラーチェックの恩恵がすぐに感じられますよ!