TypeScriptでの列挙型のモダンな代替:オブジェクト + `as const`

この方法は、従来の enum を使わずに、型安全かつ柔軟な定数列挙を実現する方法です。
as constsatisfies を併用することで、コードと型を同期させた管理が可能になり、補完性やリテラル型の厳密な保持も実現できます。


📦 基本定義

type StatusItem = {
  code: string;
  name: string;
};

const Status = {
  Get: { code: "0", name: "検索" },
  Put: { code: "1", name: "登録" },
  Post: { code: "2", name: "更新" },
} as const satisfies Record<string, StatusItem>;

🔍 as const + satisfies のポイント

  • codename の値は リテラル型(”0″ / “1” / “2”)として保持される
  • Status 全体が Record<string, StatusItem> に適合していることを型レベルで検証
  • 型崩れがあればコンパイルエラーで検出
  • as const により、プロパティはreadonly化され、IDE補完が強化

🧠 型の抽出(Mapped Types)

type StatusKey = keyof typeof Status;
// → 'Get' | 'Put' | 'Post'

type StatusUnion = typeof Status[StatusKey];
// → { code: "0", name: "検索" } | { code: "1", name: "登録" } | { code: "2", name: "更新" }

type StatusCode = StatusUnion["code"];
// → "0" | "1" | "2"

type StatusName = StatusUnion["name"];
// → "検索" | "登録" | "更新"

✅ 利用例

🔸 コード一覧の取得

const codeList: StatusCode[] = Object.values(Status).map(s => s.code);

🔸 コード → 名称変換

function codeToName(code: StatusCode): StatusName | undefined {
  return Object.values(Status).find(s => s.code === code)?.name;
}

🎯 このやり方のメリット

特徴 説明
✅ 型安全 各値が StatusItem に合致しているかを型で保証
✅ リテラル型保持 as const により "0" のようなリテラル型を維持
✅ 実行時一覧取得可能 Object.values() でコード・名称の一覧を取得できる
✅ IDE補完が強力 Status.Get.code などの補完が効く
✅ enumより軽量 実行時出力が小さく、ツリーシェイキングにも有利
✅ 多言語対応しやすい ラベル・文言などの拡張が自由自在

🛑 注意点

  • APIやDBからの外部データがある場合、型外の値が混入する可能性あり
  • バリデーションを追加することで実行時の安全性を補完するのが理想
    • 例:includes, Object.hasOwnProperty, Zod, io-ts など

コメントを残す 0

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