TypeScript 5.xの型レベルプログラミング、使いこなせてる奴ほぼいない説

$ |
TypeScript 5.xの型レベルプログラミング、使いこなせてる奴ほぼいない説
$

型レベルプログラミングとは何か

TypeScript 5.xで追加された型レベル機能、正直に言って使いこなせてる人ほぼいない。僕の周りでも「Template Literal Typesすげー」で止まってる人が大半だ。型レベルプログラミングとは、TypeScriptの型システムそのものをプログラミング言語として使うアプローチのこと。値の世界ではなく、型の世界でロジックを組む。

これができると何が嬉しいかというと、コンパイル時にバグを潰せる範囲が劇的に広がる。ランタイムエラーを型エラーに変換できるわけだ。

Conditional Typesの実践パターン

まず基本のConditional Types。これを再帰的に使うのが型レベルプログラミングの第一歩。

type DeepReadonly<T> = T extends object
  ? { readonly [K in keyof T]: DeepReadonly<T[K]> }
  : T;

// ネストしたオブジェクトも全部readonlyになる
type Config = DeepReadonly<{
  db: { host: string; port: number };
  cache: { ttl: number };
}>;

これだけで「うっかりconfigを書き換えちゃった」系のバグが完全に消える。5.xではこの再帰の深さ制限も緩和されて、実用レベルになった。

Template Literal Typesで文字列を型で縛る

APIのパスとか、特定のフォーマットの文字列を型で縛れるのがTemplate Literal Types。これがマジで強い。

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

// これはOK
const route: RouteKey = 'GET /api/users';
// これは型エラー
const bad: RouteKey = 'PATCH /api/users'; // Error!

ルーティング定義を型安全にできるのはデカい。Express使ってる人、これだけでも導入する価値ある。

satisfies演算子との組み合わせが最強

TypeScript 5.xで地味に最強なのがsatisfies演算子。型推論を保ちつつ型チェックできる。

const config = {
  api: { url: 'https://example.com', timeout: 3000 },
  db: { host: 'localhost', port: 5432 }
} satisfies Record<string, { [key: string]: string | number }>;

// config.api.url は string型として推論される(Record値型ではない)
config.api.url.toUpperCase(); // OK!

asで型を付けると推論が死ぬ問題をsatisfiesが解決した。これを知らない人がまだ多すぎる。

まとめ — 型レベルプログラミングは武器になる

正直、型レベルプログラミングは学習コストが高い。でも一度身につけたら、コードの安全性が段違いに上がる。特にライブラリ作者やフレームワーク開発者には必須スキルだと思う。「anyで逃げる」時代はもう終わり。型と向き合え。