【入門編】TypeScriptにおける「列挙型(Enum)」の代替としてのas constオブジェクトの優位性 – TypeScript コア・型システムの基礎解析バイブル

こんにちは!TypeScriptを触り始めると、「データの選択肢や状態を表すのに、どの書き方を使えばいいんだろう?」と迷う場面に出会いませんか?

他のプログラミング言語(JavaやC#、Rustなど)を触ったことがある方なら、「Enum(列挙型)」を見かけて、「よし、これを使おう!」と飛びつきがちです。でも実は、現代のTypeScript開発では、Enumではなく`as const`(アズ・コインスト)を使ったオブジェクトで代用するのが主流であり、圧倒的に安全なんです。

今回は、なぜEnumよりも`as const`オブジェクトが優れているのか、その理由を型システムの裏側まで覗き見しながら、優しく丁寧に解説していきますね。ここをクリアすれば、TypeScriptの基本はバッチリマスターできますよ!

—

1. まずは「Enum」の正体と、そのモヤモヤを知ろう

TypeScriptにおけるEnumは、名前付きの定数の集合を定義するための機能です。例えば、ユーザーの権限(ロール)を定義してみましょう。

// 伝統的なEnumの書き方
enum Role {
Admin = “ADMIN”,
Editor = “EDITOR”,
Viewer = “VIEWER”,
}

function printRole(role: Role) {
console.log(`現在のロールは ${role} です`);
}

printRole(Role.Admin); // 出力: 現在のロールは ADMIN です

一見すると綺麗にまとまっていて便利そうですよね。しかし、このEnum、実はTypeScriptの設計において「ちょっと異質な存在」なのです。

Enumの何が問題なの?

TypeScriptの基本思想は「コンパイルするとJavaScriptのコードになり、型情報は消える(TypeScript特有のランタイムコードは増やさない)」というものですが、Enumはこれに反しています。

上記のEnumをTypeScriptのコンパイラ(tsc)に通すと、JavaScriptでは次のようなオブジェクトに変換(トランスパイル)されます。

// コンパイル後のJavaScriptコード
var Role;
(function (Role) {
Role[“Admin”] = “ADMIN”;
Role[“Editor”] = “EDITOR”;
Role[“Viewer”] = “VIEWER”;
})(Role || (Role = {}));

おっと…? 余計なIIFE(即時関数)やオブジェクトが生成されてしまいましたね。これが、「バンドルサイズ(最終的なJSファイルの容量)が無駄に膨らむ原因」になります。さらに、JavaScriptの通常のオブジェクトとは挙動が少し特殊で、他のライブラリやJSONとのやり取りで混乱しやすいというデメリットもあります。

—

2. 救世主登場! `as const` によるオブジェクトの定数化

そこで登場するのが、今回の主役である `as const` です。
これを使うと、普通のJavaScriptのオブジェクトを、TypeScriptの型システムにとって「これ以上変更できない、極限まで厳格な定数(Readonlyかつリテラル型)」に仕立て上げることができます。

百聞は一見にしかず、書き方を見てみましょう。

// as const を使ったオブジェクト定義
const Role = {
Admin: “ADMIN”,
Editor: “EDITOR”,
Viewer: “VIEWER”,
} as const; // ← ここが魔法の呪文!

// 型を自動で抽出するテクニック
type Role = typeof Role[keyof typeof Role];
// 出来上がった型: “ADMIN” | “EDITOR” | “VIEWER” のユニオン型になります

コードの意味を解剖するよ

1. `as const`(常数アサーション):
オブジェクトのプロパティをすべて `readonly`(読み取り専用)にし、値もただの `string` 型ではなく、`”ADMIN”` や `”EDITOR”` といった具体的なリテラル型として固定します。
2. `typeof Role[keyof typeof Role]`:
「`Role`オブジェクトの値(Values)の集合体」を型として取り出しています。これで、”`ADMIN`か`EDITOR`か`Viewer`のどれかしか受け付けない”という強力な型が完成します。

—

3. なぜ `as const` の方が圧倒的に優れているのか?

Enumと比較したときの、`as const`オブジェクトの主な優位性は以下の3点です。

① 余分なJavaScriptコードが出力されない(バンドルサイズに優しい)

`as const`で定義したオブジェクトは、TypeScriptの型チェックが終わったあと、ただのプレーンなJavaScriptのオブジェクトになります。Enumのように謎の関数が生成されないため、アプリの軽量化に直結します。

② JavaScriptの標準エコシステムと完全に調和する

EnumはTypeScript独自の構文であり、JavaScriptの世界から見ると少し特殊です。一方、`as const`は「ただのJavaScriptのオブジェクト」なので、そのままAPIのペイロードとして投げたり、`Object.keys()`や`Object.values()`などの標準メソッドと組み合わせたりするときに、型推論がパーフェクトに効きます。

③ 「値」としても「型」としてもシームレスに扱える

ここが実務で一番嬉しいポイントです。

const Role = {
Admin: “ADMIN”,
Editor: “EDITOR”,
Viewer: “VIEWER”,
} as const;

// 関数に「値」として渡すとき
const myRole = Role.Admin; // “ADMIN”

// 関数に「型」として指定するとき
function transitionRole(newRole: typeof Role[keyof typeof Role]) {
// 処理…
}

一つの定義から「値(`Role.Admin`)」も「型(`typeof Role[keyof typeof Role]`)」も綺麗に取り出せるため、コードの重複がなくなります。

—

4. 陥りやすい文法エラーと注意点

初心者の頃によくやってしまうミスを先回りしてシェアしておきますね。ここを知っておくとスムーズです。

罠:`as const` を付け忘れる

// ❌ NGな例
const Role = {
Admin: “ADMIN”,
Editor: “EDITOR”,
};

// as constがないと、これらはただの string 型と推論されてしまう!
type RoleType = typeof Role[keyof typeof Role]; // string 型になってしまう

対策: オブジェクトの末尾には必ず `as const` をつける癖をつけましょう!これによって、型が `”ADMIN”` というピンポイントなリテラル型に固定されます。

—

まとめ:これからのTypeScriptは `as const` で決まり!

今回は、Enumの代替としての `as const` オブジェクトの優位性について、背景の仕組みから実践的なコードまで解説しました。

  • Enum: 伝統的だが、余分なJSコードが生成され、JSの標準エコシステムと少し相性が悪い。
  • `as const` オブジェクト: 動作が軽量で、JSの作法に忠実でありながら、型システムとも完璧に調和する現代のベストプラクティス。

迷ったら、まずは 「プレーンなオブジェクト + `as const` + `typeof` による型の抽出」 のコンボを使ってみてください。型安全でスッキリとした、美しいTypeScriptコードが書けるようになりますよ。

今日の学びを活かして、ぜひ明日のコードをより洗練されたものにアップデートしてみてくださいね。応援しています!

タイトルとURLをコピーしました