📦

TypeScript で enum は不要? なにそれ?

2025/02/28に公開

TypeScript は JavaScript に静的型付けのパワーをもたらす言語として、フロントエンドからバックエンドまで幅広く利用されています。そんな TypeScript の機能の一つとして、enum(列挙型)があります。enum は、あらかじめ決まっている複数の値をまとめて定義する仕組みです。

本記事では、TypeScript の enum がどのように役立つのか、そのメリットやユースケース、さらに注意点や代替案との比較を解説していきます。


1. enum とは?

enumは、「あらかじめ決まっている複数の値をまとめて定義するための機能」です。たとえば、ユーザーのロール(Admin, Editor, Viewer など)、注文状態(Pending, Shipped, Delivered など)、曜日など、選択肢が限られている値を表すときに使われます。

enum の主な種類

TypeScript には、以下の 3 種類の enum があります。

  1. 数値 enum
  2. 文字列 enum
  3. const enum(定数 enum)

数値 enum

enum UserRole {
  Admin,      // 0
  Editor,     // 1
  Viewer,     // 2
}

const role: UserRole = UserRole.Editor;
console.log(role);          // 1
console.log(UserRole[role]); // "Editor"

最初のメンバーには 0 が割り当てられ、その後自動的に 1, 2 とインクリメントされます。必要に応じて手動で値を指定することもできます。

文字列 enum

enum OrderStatus {
  Pending = "PENDING",
  Shipped = "SHIPPED",
  Delivered = "DELIVERED",
  Canceled = "CANCELED",
}

const status: OrderStatus = OrderStatus.Shipped;
console.log(status); // "SHIPPED"

文字列 enum は、コンパイル後のコードでも値のまま扱われるため、デバッグ時や API 通信のやり取りで値がわかりやすいのが特徴です。

const enum

const enum Direction {
  Up,
  Down,
  Left,
  Right,
}

const dir = Direction.Up;

const enum を使うと、コンパイル後には実際の enum オブジェクトを生成せず、定数値にインライン展開されます。オブジェクト参照が不要な分、パフォーマンス面でのメリットがあります。ただし、ランタイムに参照が必要な場合(たとえば Object.keys() で列挙子の一覧を取りたいなど)には使えないので注意が必要です。


2. enum を使うメリット

2-1. コードの可読性が高まる

enum を使うことで、限られた選択肢を示す名前をプログラム中で明示できます。
たとえば、ユーザーのロールを数値や文字列だけで書くよりも、UserRole.AdminUserRole.Editor といった形にするほうがロジック上の意図がはっきりします。

2-2. 型安全を担保できる

enum を使うと、定義したメンバー以外の値が入り込むことをコンパイラが防いでくれます。これによって、誤った値の代入から生じるバグを減らすことができます。

enum UserRole {
  Admin,
  Editor,
  Viewer,
}

function canEdit(role: UserRole): boolean {
  return role === UserRole.Admin || role === UserRole.Editor;
}

// canEdit(10); // エラー: 10 は UserRole のいずれでもない

2-3. エディタの補完が便利

enum を定義しておくと、VSCode などのエディタで補完(IntelliSense)が効きやすくなり、開発効率も高まります。書き間違えの防止にもつながります。


3. 典型的なユースケース

3-1. ステータス管理

注文のステータス、ユーザーのアクティブ状態、タスクの状況など、「切り替え先があらかじめ決まっている状態」をまとめるのに便利です。

enum TaskStatus {
  Todo = "TODO",
  InProgress = "IN_PROGRESS",
  Done = "DONE",
}

function updateTaskStatus(taskId: string, status: TaskStatus) {
  // ステータスを更新するロジック
}

updateTaskStatus("task01", TaskStatus.InProgress);

3-2. API のパラメータやレスポンスとの連携

バックエンドとの通信において、ステータスやフラグなどを文字列でやり取りすることが多い場合、enum によってフロントエンド側でも型安全に扱うことができます。

3-3. 並び替えやソートの指定

enum SortDirection {
  Asc = "asc",
  Desc = "desc",
}

function getUsers(sort: SortDirection) {
  // ...
}

getUsers(SortDirection.Asc);

並び順を "asc" と "desc" のどちらかしか使わない場面でも enum は有効です。


4. Union Type との比較

TypeScript で「特定のいくつかの値だけを許可する」方法としては、enum 以外に Union Type(リテラル型の合併)もよく使われます。

type Status = "PENDING" | "SHIPPED" | "DELIVERED" | "CANCELED";

function setOrderStatus(status: Status) {
  // ...
}

union type でも enum と同様に「決められた値だけ」を強制できます。最近では、「文字列を直接扱える」「JavaScript 的に自然」などの理由で union type のほうが好まれるケースもあります。

一方で、enum には以下のような利点もあります。

  • enum オブジェクトとして扱える
    たとえば OrderStatus["Pending"] のようにメンバー名から参照できる。
  • リファクタリングのしやすさ
    文字列の変更を enum に閉じ込めておけるので、修正を一箇所に集中できる。
  • チームの慣れや言語横断的な対応
    C# や Java など、enum に親しみのあるメンバーがいる場合、自然に導入できる。

ただし、enum はコンパイル後にオブジェクトが生成される(const enum でない場合)など、プロジェクトの構成やパフォーマンス要件に応じて検討すべき点があります。


5. enum を使う上での注意点

5-1. 乱用しない

「あらかじめ決まっている複数の値」を表現したいときは、enum 以外にも Union Typeオブジェクト など選択肢があります。チームのコーディング規約やプロジェクトの方針を考慮しながら、enum を活用するかどうかを判断しましょう。

5-2. const enum の制約

const enum はコンパイル後に定数としてインライン展開されるため、実行時にオブジェクトとしての enum が存在しません。そのため、列挙子を Object.keys() で取り出したい場合など、ランタイムで enum 本体が必要になるケースでは適していません。必要に応じて通常の enum を使い分けましょう。

5-3. tsconfig の設定に注意

プロジェクトの tsconfig.jsonisolatedModules を有効にしていると、const enum にまつわる警告が出る場合があります。他にも preserveConstEnumsskipLibCheck の設定次第で挙動が変わるので、enum を使う際はビルド設定に注意しましょう。


6. まとめ

TypeScript の enum は、

  • 限られた選択肢をまとめて分かりやすく定義できる
  • 型安全を強化する
  • エディタの補完など、開発効率を上げられる

といったメリットがあります。一方で、enum は絶対的な正解ではなく、Union Type やオブジェクトリテラルなどの選択肢も存在します。どちらが最適かは、チームの慣習・プロジェクトの要件・可読性などによって異なるでしょう。

この記事を参考に、プロジェクトに合った方法で列挙的なデータを管理し、TypeScript の型安全性と開発効率をさらに高めてみてください。

Discussion