TypeScript で enum は不要? なにそれ?
TypeScript は JavaScript に静的型付けのパワーをもたらす言語として、フロントエンドからバックエンドまで幅広く利用されています。そんな TypeScript の機能の一つとして、enum(列挙型)があります。enum は、あらかじめ決まっている複数の値をまとめて定義する仕組みです。
本記事では、TypeScript の enum がどのように役立つのか、そのメリットやユースケース、さらに注意点や代替案との比較を解説していきます。
1. enum とは?
enumは、「あらかじめ決まっている複数の値をまとめて定義するための機能」です。たとえば、ユーザーのロール(Admin, Editor, Viewer など)、注文状態(Pending, Shipped, Delivered など)、曜日など、選択肢が限られている値を表すときに使われます。
enum の主な種類
TypeScript には、以下の 3 種類の enum があります。
- 数値 enum
- 文字列 enum
- 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.Admin
や UserRole.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 を活用するかどうかを判断しましょう。
const enum
の制約
5-2. const enum
はコンパイル後に定数としてインライン展開されるため、実行時にオブジェクトとしての enum が存在しません。そのため、列挙子を Object.keys()
で取り出したい場合など、ランタイムで enum 本体が必要になるケースでは適していません。必要に応じて通常の enum を使い分けましょう。
5-3. tsconfig の設定に注意
プロジェクトの tsconfig.json
で isolatedModules
を有効にしていると、const enum
にまつわる警告が出る場合があります。他にも preserveConstEnums や skipLibCheck の設定次第で挙動が変わるので、enum を使う際はビルド設定に注意しましょう。
6. まとめ
TypeScript の enum は、
- 限られた選択肢をまとめて分かりやすく定義できる
- 型安全を強化する
- エディタの補完など、開発効率を上げられる
といったメリットがあります。一方で、enum は絶対的な正解ではなく、Union Type やオブジェクトリテラルなどの選択肢も存在します。どちらが最適かは、チームの慣習・プロジェクトの要件・可読性などによって異なるでしょう。
この記事を参考に、プロジェクトに合った方法で列挙的なデータを管理し、TypeScript の型安全性と開発効率をさらに高めてみてください。
Discussion