TypeScript Enum vs Union Type、どちらを使うべきか

問題

TypeScriptプロジェクトで定数の集合を定義する際、enumunion typeのどちらを使うべきか悩むことがあります。チームによって規約も異なり、判断が難しいところです。

Enumの落とし穴

enum Status {
  Active = 'ACTIVE',
  Inactive = 'INACTIVE',
  Pending = 'PENDING'
}

これがJavaScriptにコンパイルされると、以下のようなコードが生成されます。

var Status;
(function (Status) {
  Status["Active"] = "ACTIVE";
  Status["Inactive"] = "INACTIVE";
  Status["Pending"] = "PENDING";
})(Status || (Status = {}));

ランタイムにオブジェクトが生成されます。バンドルサイズに影響し、tree-shakingもうまく機能しません。

解決方法:Union Type + as const

const STATUS = {
  Active: 'ACTIVE',
  Inactive: 'INACTIVE',
  Pending: 'PENDING',
} as const;

type Status = typeof STATUS[keyof typeof STATUS];
// 結果: 'ACTIVE' | 'INACTIVE' | 'PENDING'

as constを付けることで、リテラル型として推論されます。ランタイムコードが最小限になり、tree-shakingも正常に動作します。

シンプルな場合は、直接union typeを使うだけで十分です。

type Direction = 'up' | 'down' | 'left' | 'right';

ポイント

  • enumはランタイムオブジェクトを生成し、バンドルサイズが増加します
  • const enumはインライン化されますが、--isolatedModules環境(Vite、Next.jsなど)では使用できません
  • as const + union typeがほとんどのケースでより適切です
  • enumが必要な場面:ビットフラグ演算やランタイムでの逆方向マッピングが必要な場合に限られます