TypeScriptでの列挙型のモダンな代替:オブジェクト + `as const`
この方法は、従来の enum
を使わずに、型安全かつ柔軟な定数列挙を実現する方法です。
as const
と satisfies
を併用することで、コードと型を同期させた管理が可能になり、補完性やリテラル型の厳密な保持も実現できます。
📦 基本定義
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
のポイント
code
やname
の値は リテラル型(”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
など
- 例: