TypeScript 列挙型(enum)の代替案

TypeScript の enum は便利ですが、以下の理由で使わないことが推奨されることがあります:

  • JavaScriptに変換されると余計なコードが出力される
  • 値の双方向マッピングが不要な場合が多い
  • ツリーシェイキングに不利
  • 型安全・補完の観点で as const + Union型の方が有利

✅ 代替案 1:as const + Union型

const Status = {
  Draft: 'draft',
  Published: 'published',
  Archived: 'archived',
} as const;

type Status = typeof Status[keyof typeof Status];

// 使用例
function canEdit(status: Status) {
  return status === Status.Draft;
}

✔ 特徴

  • 実行時値と型を一致させられる
  • Object.values(Status) で一覧取得可能
  • IDE補完が効く
  • 型安全

✅ 代替案 2:Unionリテラル型

type Status = 'draft' | 'published' | 'archived';

function canEdit(status: Status) {
  return status === 'draft';
}

✔ 特徴

  • 最もシンプル
  • IDE補完あり
  • 軽量で読みやすい

❗ 注意

  • 実行時に列挙値一覧を取得するには別途定数が必要

✅ 代替案 3:配列定数 + typeof T[number]

const Roles = ['admin', 'editor', 'viewer'] as const;
type Role = typeof Roles[number];

// 使用例
function hasWriteAccess(role: Role) {
  return role !== 'viewer';
}

✔ 特徴

  • 実行時の一覧と型定義を一括管理可能
  • ループやincludesチェックができる
if (Roles.includes('admin' as Role)) {
  console.log('Valid role');
}

✅ 代替案 4:マッピングオブジェクト(Map風)

const Labels = {
  admin: '管理者',
  editor: '編集者',
  viewer: '閲覧者',
} as const;

type Role = keyof typeof Labels;

✔ 特徴

  • i18n(国際化)や表示名に便利
  • Record<Role, string> 型で自動補完も可能

enum の落とし穴(例)

enum Status {
  Draft,       // 0
  Published,   // 1
  Archived     // 2
}

const s = Status.Draft; // s === 0

❗ 問題点

  • 値が数字になりやすくバグの原因に
  • Status[0] === 'Draft' のように逆引きできるが、これは多くの場合不要
  • 実行時コードが増える(enumはJSでオブジェクトになる)

✅ まとめ:どれを使うべきか?

目的 おすすめ代替法
単純な状態管理 Union型(例: 'open' | 'closed'
実行時に一覧や表示が必要 as const + typeof
ユーザー定義型の管理 定数配列 + typeof T[number]
UI表示(ラベル)付き状態管理 マッピングオブジェクト(Record)

🧾 補足:厳密な型安全のための tsconfig 設定

{
  "compilerOptions": {
    "strict": true,
    "preserveConstEnums": false,
    "isolatedModules": true
  }
}
コメントを残す 0

Your email address will not be published. Required fields are marked *