TypeScriptを使っていると、「ステータスを 0・1・2 で管理するのは辛い」「文字列定数をまとめたい」という場面が必ず出てきます。そこで役立つのが enum(列挙型) です。
enumを使うと関連する定数をひとまとめにできますが、「数値enumと文字列enumの違いは?」「const enum はいつ使う?」「union型や as const と何が違うの?」という疑問も多いです。本記事では、TypeScript の enum を基礎から実務パターンまで体系的に解説します。
この記事で学べること
- 数値 enum・文字列 enum・ヘテロジニアス enum の基本と違い
- const enum によるコンパイル最適化の仕組み
- 数値 enum の逆引き(reverse mapping)の使い方
- enum を型として使う・型の絞り込みとの組み合わせ
- enum vs union 型 vs const assertion(as const)の使い分け
- ステータス管理・API レスポンス分岐など実務でよく使うパターン
- よくあるエラーと解決方法
数値 enum の基本
enum キーワードで定義し、メンバーにはデフォルトで 0 から始まる整数が自動割り当てられます。
数値 enum の基本構文
// 基本的な数値 enum
enum Direction {
Up // 0
Down // 1
Left // 2
Right // 3
}
// アクセス方法
const dir: Direction = Direction.Up; // 0
console.log(Direction.Up); // 0
console.log(Direction[0]); // "Up"(逆引き)
開始値と手動割り当て
開始値を変更・手動で値を指定
// 1 始まりにする
enum Status {
Pending = 1,
Active = 2,
Inactive = 3,
}
// 一部だけ指定(後続は自動インクリメント)
enum HttpStatus {
OK = 200,
NotFound = 404,
ServerError = 500,
}
文字列 enum
各メンバーに文字列リテラルを割り当てます。数値 enum と異なり逆引きは存在しませんが、デバッグ時に値が意味をなすため実務でよく使われます。
文字列 enum の基本
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
console.log(Color.Red); // "RED"
console.log(Color["RED"]); // undefined(文字列enumに逆引きなし)
// API レスポンスの方向性を定義するケース
enum SortOrder {
Asc = "asc",
Desc = "desc",
}
function getUsers(order: SortOrder) {
return fetch(`/api/users?sort=${order}`);
}
getUsers(SortOrder.Asc);
文字列 enum が数値 enum より好まれる理由:数値 enum はデバッグ時に 0 や 1 という値しか見えません。文字列 enum なら "asc"・"desc" のように意味が明確で、ログや API レスポンスで実際の値を確認しやすくなります。
const enum(コンパイル時最適化)
const enum はコンパイル時にメンバーの値がインライン展開され、生成される JavaScript にオブジェクトが残りません。パフォーマンスに敏感な場合に有効です。
通常 enum vs const enum の違い
// 通常 enum → コンパイル後にオブジェクトが残る
enum Direction { Up, Down, Left, Right }
const d = Direction.Up; // → Direction.Up(参照が残る)
// const enum → コンパイル後にインライン展開
const enum Speed {
Slow = 1,
Normal = 2,
Fast = 3,
}
const s = Speed.Fast; // → const s = 3;(リテラルに展開)
const enum の制限:Babel や esbuild でトランスパイルする場合、const enum は正しく処理されないことがあります(Babel はデフォルトで const enum 非対応)。tsconfig の isolatedModules: true 環境では const enum はエラーになります。バンドラーを使うプロジェクトでは通常の enum か union 型を推奨します。
数値 enum の逆引き(Reverse Mapping)
数値 enum はコンパイル後に値からメンバー名を取得できる逆引きテーブルが自動生成されます。文字列 enum にはこの機能はありません。
逆引きの使い方
enum Status {
Pending = 1,
Active = 2,
Done = 3,
}
// 数値 → 名前(逆引き)
console.log(Status[1]); // "Pending"
console.log(Status[2]); // "Active"
// 動的な値からラベル取得
function getStatusLabel(code: number): string {
return Status[code] ?? "Unknown";
}
getStatusLabel(2); // "Active"
getStatusLabel(9); // "Unknown"
コンパイル後の JavaScript(逆引きテーブルの仕組み)
コンパイル後の JS(参考)
// TypeScript の enum は以下のような JS に変換される
var Status;
(function (Status) {
Status[Status["Pending"] = 1] = "Pending";
Status[Status["Active"] = 2] = "Active";
Status[Status["Done"] = 3] = "Done";
})(Status || (Status = {}));
// Status[1] = "Pending" かつ Status["Pending"] = 1 の双方向マップが生成される
enum を型として使う
enum は型としても機能します。引数の型に enum を指定すると、定義されたメンバー以外の値を渡せなくなります。
enum を型として引数・戻り値に使う
enum UserRole {
Admin = "admin",
Editor = "editor",
Viewer = "viewer",
}
function checkPermission(role: UserRole): boolean {
return role === UserRole.Admin;
}
checkPermission(UserRole.Admin); // OK
// checkPermission("admin"); // TS Error: Argument of type 'string' is not assignable
// checkPermission("superuser"); // TS Error
switch 文との組み合わせ(網羅性チェック)
switch + never で網羅性を保証
function handleRole(role: UserRole): string {
switch (role) {
case UserRole.Admin: return "管理者";
case UserRole.Editor: return "編集者";
case UserRole.Viewer: return "閲覧者";
default:
const _exhaustive: never = role; // 未処理ケースがあればコンパイルエラー
return _exhaustive;
}
}
never による網羅性チェック:switch 文の default ブランチで never 型変数に代入することで、enum に新しいメンバーを追加した際に「対応する case がない」とコンパイルエラーで検知できます。実務でのバグ防止に非常に有効なパターンです。
enum vs union 型 vs const assertion の使い分け
TypeScript で定数群を管理する方法は enum だけではありません。union 型と const assertion(as const)も広く使われます。それぞれの特徴を理解して使い分けましょう。
3種類の定数管理方法の比較
// ① enum
enum ColorEnum { Red = "red", Blue = "blue" }
type T1 = ColorEnum; // ColorEnum 型
// ② union 型(文字列リテラル型)
type ColorUnion = "red" | "blue";
type T2 = ColorUnion; // "red" | "blue"
// ③ const assertion(as const)
const COLOR = { Red: "red", Blue: "blue" } as const;
type T3 = typeof COLOR[keyof typeof COLOR]; // "red" | "blue"
| 観点 |
enum |
union 型 |
const assertion |
| JS 出力 |
○(オブジェクト生成) |
×(型のみ) |
○(オブジェクト残る) |
| 逆引き |
○(数値のみ) |
× |
× |
| 補完 |
○ |
○ |
○ |
| 型安全性 |
○ |
◎(より厳密) |
◎ |
| ランタイムで値参照 |
○ |
× |
○ |
| Babel/esbuild 互換 |
△(const enumは注意) |
◎ |
◎ |
| おすすめ場面 |
レガシー・逆引きが必要な場合 |
型だけで十分な場合 |
実務での第一選択肢 |
モダンな TypeScript では union 型や const assertion が好まれます。enum は生成される JS コードが増え、Babel との互換性問題もあるため、新規プロジェクトでは as const + union 型の組み合わせを推奨する意見が増えています。ただし、逆引きが必要な場合やレガシーコードとの互換性を保つ場合は enum も有効です。
const assertion を使った enum の代替パターン
as const で enum を代替する
// const assertion を使った enum 代替
const UserRole = {
Admin: "admin",
Editor: "editor",
Viewer: "viewer",
} as const;
// 値の union 型を取得
type UserRoleType = typeof UserRole[keyof typeof UserRole];
// type UserRoleType = "admin" | "editor" | "viewer"
// 使用例
function checkPermission(role: UserRoleType): boolean {
return role === UserRole.Admin;
}
実務でよく使うパターン
API レスポンスのステータス管理
HTTP ステータスコードの enum
enum ApiStatus {
Success = 200,
Created = 201,
BadRequest = 400,
Unauthorized = 401,
NotFound = 404,
ServerError = 500,
}
function handleResponse(status: ApiStatus) {
switch (status) {
case ApiStatus.Success:
case ApiStatus.Created:
return "成功";
case ApiStatus.Unauthorized:
return "認証エラー";
case ApiStatus.NotFound:
return "見つかりません";
default:
return "エラーが発生しました";
}
}
フォームの入力状態管理(文字列 enum)
フォーム状態管理
enum FormState {
Idle = "idle",
Submitting = "submitting",
Success = "success",
Error = "error",
}
// React のステート管理との組み合わせ
const [state, setState] = useState<FormState>(FormState.Idle);
async function handleSubmit() {
setState(FormState.Submitting);
try {
await submitForm();
setState(FormState.Success);
} catch {
setState(FormState.Error);
}
}
ビットフラグ(フラグ列挙)
権限管理など、複数のフラグを組み合わせて管理したい場合は2の累乗を使ったビットフラグが有効です。
ビットフラグ enum
enum Permission {
None = 0,
Read = 1 << 0, // 1
Write = 1 << 1, // 2
Delete = 1 << 2, // 4
Admin = Read | Write | Delete, // 7
}
// 権限チェック(ビット AND)
function hasPermission(userPerm: Permission, required: Permission): boolean {
return (userPerm & required) === required;
}
const userPerm = Permission.Read | Permission.Write; // 3
hasPermission(userPerm, Permission.Read); // true
hasPermission(userPerm, Permission.Delete); // false
よくあるエラーと解決方法
| エラー / 問題 |
原因 |
解決方法 |
Element implicitly has an 'any' type(逆引き時) |
数値インデックスの型が未定義 |
enum[value as number] または type assertion を使う |
const enum が undefined になる |
Babel でトランスパイル時に const enum 未対応 |
通常の enum か union 型に変更。tsconfig に verbatimModuleSyntax 等を設定 |
| 文字列 enum に逆引きができない |
文字列 enum は reverse mapping を持たない仕様 |
数値 enum にするか、別途 Record でマップを作成 |
enum の値が any になる(型引数で) |
enum を型変数の制約に使う場合の制限 |
T extends MyEnum ではなく union 型で制約を書く |
よくあるエラーの修正例
// ❌ 文字列 enum で逆引き → undefined
enum Color { Red = "red" }
console.log(Color["red"]); // undefined(逆引き不可)
// ✅ 解決: Record で逆引きマップを自前で作る
const ColorLabel: Record<Color, string> = {
[Color.Red]: "赤",
};
ColorLabel[Color.Red]; // "赤"
// ❌ 数値 enum に存在しない値を渡す(型エラーにならないケース)
enum Dir { Up = 0, Down = 1 }
const d: Dir = 99; // TypeScriptは数値なら代入を許す(注意)
// ✅ 解決: 文字列 enum にするか union 型にして型安全性を高める
enum StrDir { Up = "up", Down = "down" }
// const d: StrDir = "invalid"; // NG
まとめ
TypeScript enum 完全まとめ
- 数値 enum:0 始まりの整数が自動割り当て。逆引き(reverse mapping)が使える
- 文字列 enum:意味のある文字列を割り当て。デバッグしやすく実務で人気。逆引きなし
- const enum:コンパイル時にインライン展開。Babel 環境では注意が必要
- switch + never で enum の網羅性チェック。新メンバー追加漏れをコンパイル時に検出
- 数値 enum は型が緩い(任意の数値を代入できる)。型安全性が重要なら文字列 enum か union 型を推奨
- モダンな選択肢:union 型や const assertion(as const)は型のみで実現でき、JS の出力コードを減らせる
- 逆引きが必要・ランタイムでキー列挙したい場合は enum が依然有効
enum の使い分けに迷ったら、まずユーティリティ型の Record や高度な型の const assertion との組み合わせを検討してみてください。また、switch 文での型ガードについては型の絞り込み(Type Narrowing)も参照ください。
よくある質問(FAQ)
enum と union 型はどちらを使えばいいですか?
モダンな TypeScript では union 型(文字列リテラル型)か const assertion を推奨します。enum は JS コードが増え、Babel との互換性問題もあります。ただし、ランタイムでキーや値を列挙したい場合・逆引きが必要な場合・レガシーコードとの互換性が必要な場合は enum が適しています。
数値 enum に任意の数値を代入できてしまうのはなぜですか?
TypeScript の数値 enum は数値型の拡張であり、型チェックが緩くなっています(例: Status = 99 がエラーにならない)。これは意図的な仕様ですが、型安全性を重視するなら文字列 enum か union 型のリテラル型を使うと厳密になります。
const enum はいつ使うべきですか?
tsc のみでコンパイルし、Babel や esbuild を使わないプロジェクトで、かつパフォーマンス最適化が必要な場合に限定して使用を検討してください。tsconfig に isolatedModules: true がある場合は const enum は使えません。迷ったら通常の enum を使いましょう。
enum のメンバーをオブジェクトのキーとして使えますか?
はい。Record<MyEnum, string> や computed property({ [MyEnum.Key]: value })の形で使えます。文字列 enum は特にオブジェクトキーとして使いやすく、型安全な辞書を作る場面でよく活用されます。