C# の enum 感覚で詰まる TypeScript の enum(数値 / 文字列 / const / union の違い)
みなさんこんにちは!ヒロポンです!
「来月からフロントも TypeScript でお願い」。
業務系一筋でやってきた身に、これがいきなり降ってくると地味に身構えますよね。
私も最初、C# の enum のノリで TypeScript の enum を書きました。で、生成された JavaScript を見て「あれ??なんかオブジェクトができてる」と固まった。
この記事は、TypeScript enum C# の感覚のズレで詰まる人向けです。C# の enum を基準に、TS の数値 enum・文字列 enum・const enum・union 型リテラルがそれぞれ何に対応するのか、対応マップと実コードで整理していきます。
先に言っておくと、C# の知識はムダになりません。「C# のあれが TS だとこれ」という対応さえ掴めば、TS の enum 周りはだいたい読めるようになります。
C# の enum のつもりで書くと「あれ?」となる場面
C# の enum は、コンパイル時に確定する整数の名前付き定数。実行時に何かオブジェクトが生えたりはしません。
ところが TS の数値 enum を書いて、出力された JS を見ると。
オブジェクトが1個できています。しかも値から名前を逆引きできる。
C# 脳だと「なんでランタイムに実体があるんだ??」となる。ここが最初の段差です。
私はこれを知らずに、enum を大量に定義したモジュールのバンドルサイズを見て「意外と重いな」と首をひねったことがあります。原因は、enum が実行時オブジェクトとして全部バンドルに乗っていたから。
まずは全体像を対応マップで掴みます。
C# enum ↔ TypeScript の対応マップ
C# の enum が、TS の4つの表現にどう対応するか。並べてみます。

ざっくり言うと、C# の enum の代わりに TS でまず考えるのは union 型です。
実行時オブジェクトを作らないぶん軽くて、型安全も C# の enum と同じくらい効きます。
逆引きや文字列表現が要る時だけ enum を使う。このくらいの温度感が、現場では扱いやすい。
順番に対応を見ていきます。
対応1: C# の数値 enum ↔ TS の数値 enum
C# の素直な enum は、TS でもそのまま数値 enum で書けます。見た目はほぼ同じ。
// C# の enum Status { Todo, Doing, Done } に相当
enum Status {
Todo, // 0
Doing, // 1
Done, // 2
}
console.log(Status.Todo); // 0
console.log(Status[0]); // "Todo" ← 値から名前を逆引きできる
実行結果:
![Status.Todo が 0、Status[0] が Todo と逆引きされる実行結果](/_next/image/?url=https%3A%2F%2Fcms.hiropon-progra.com%2Fwp-content%2Fuploads%2F2026%2F09%2Fblock-1-12.png&w=1200&q=75)
Status.Todo が 0 なのは C# と同じ。
でも Status[0] で "Todo" が返るのは、C# に無い挙動です。TS の数値 enum は、実行時にこんな感じの双方向オブジェクトを生成しているから。
// 上の enum が生成する JS のイメージ
var Status = {
"0": "Todo", "1": "Doing", "2": "Done",
Todo: 0, Doing: 1, Done: 2,
};
逆引きが要る場面ではこれが効きます。数値で DB に保存した値を、画面に名前で出す、みたいなやつ。
逆に、要らないなら。この実体はただの重りになります。
対応2: C# の文字列運用 ↔ TS の文字列 enum / union 型
C# で「区分を文字列で持ちたい」時、enum を ToString() したり、定数文字列を並べたりしますよね。
TS だと選択肢が2つあります。
// (A) 文字列 enum:実行時オブジェクトは作るが、逆引きは無い
enum Color {
Red = "RED",
Green = "GREEN",
}
console.log(Color.Red); // "RED"
// (B) union 型リテラル:実行時オブジェクトを作らない
type Size = "S" | "M" | "L";
const picked: Size = "M"; // "S" | "M" | "L" 以外はコンパイルエラー
実行結果(文字列 enum の値と、union 型の代入):

(B) の union 型が、TS だと一番出番が多い書き方です。
type Size = "S" | "M" | "L" と書くだけ。C# の enum と同じく「決まった値しか入らない」型安全が、いい感じに得られます。しかも実行時に何も生成しないので軽い。
「文字列 enum と union、どっちにするか??」で迷ったら、実行時にオブジェクトが要るかどうかで決めると楽です。要らなければ union。
対応3: C# の [Flags] ↔ TS は自前でビット演算
ここが一番ハマるところ。C# の [Flags] 属性に相当する仕組みは、TS の enum にはありません。
// [Flags] は無いので、数値 enum + ビット演算で自前で組む
enum Permission {
None = 0,
Read = 1 << 0, // 1
Write = 1 << 1, // 2
Admin = 1 << 2, // 4
}
const p = Permission.Read | Permission.Write; // 3
const canWrite = (p & Permission.Write) === Permission.Write; // true
console.log(p, canWrite); // 3 true
実行結果:

ビット演算(| & <<)そのものは、C# と同じ書き方で動きます。
違うのは、C# の [Flags] が持っている「ToString() でフラグ名を Read, Write と自動連結する」挙動が TS には無いこと。名前で出したいなら、そこは自分でループを書いて組み立てます。
[Flags] のつもりで enum に属性を付けようとして「あれ、そんな構文無い」となるやつ。先に知っておくと詰まりません。
ミニマム検証: 数値 enum を union に書き換えてみる
「まず union に寄せる」の感触を掴むハンズオン。数値 enum を union に置き換えると、こう変わります。

after 側は、実行時オブジェクトを一切作りません。
label の引数は、相変わらず「決まった値しか受け付けない」型のまま。C# の enum で守っていた型安全は、union でもそのまま維持できます!!
ハマりポイント: 知らないと地味に効くやつ
C# 脳のまま書くと踏むやつを2つ。
① 数値 enum は実行時オブジェクトぶん重い
対応1で見たとおり、数値 enum も文字列 enum も、実行時に双方向のオブジェクトを生成します。enum を大量に定義すると、そのぶんバンドルに乗る。
「型を縛りたいだけ」なのに enum を並べていると、フロントのバンドルが地味に膨らみます。
逆引きが要らないなら union に寄せる。これだけで無駄が減ります。
② const enum は「軽いけど配布で困る」ことがある
「実行時オブジェクトが嫌なら const enum があるじゃん」と思うかもしれません。
const enum はコンパイル時に値へインライン展開されるので、実行時オブジェクトを作りません。軽い。
ただし、isolatedModules(Babel や一部のバンドラが要求する設定)の下では、const enum に制約が出ます。ライブラリとして配布するコードだと、const enum を避けて union で書く、という判断になることが多い。
「軽いから」で安易に全部 const enum。これは後で刺さることがあります。
私の現場メモ: C# 出身者が enum で迷わないコツ
キャッチアップしていて一番効いたのは、「TS の enum は C# の enum と別物」と割り切ったことでした。
名前が同じだから同じものだと思って読むと、実行時オブジェクトの有無でいちいち引っかかる。
「TS の enum は、実行時に実体を持つ特殊な子」「型を縛りたいだけなら union」。この2軸で見るようになってから、迷わなくなりました。
もう1つ。C# の [Flags] みたいな「言語が面倒を見てくれる機能」は、TS だと自前で書くことが多いです。
これは TS が劣ってるという話じゃなくて、設計思想の違い。C# の便利機能に慣れた身だと最初は面倒に感じますが、「ここは手書きするゾーン」と割り切ると、逆に何が起きてるかいい感じに見通せます。
C# でビット演算や型の縛りを普通に書けていた人なら、TS の enum 周りは十分に読めます。既知の知識が、そのまま橋渡しになる領域です。
まとめ
C# 出身者向けに、TS の enum を整理し直すと要点はこうです。
- TS の数値 enum は実行時オブジェクトを生成し、値から名前を逆引きできる(C# に無い挙動)
- 型を縛りたいだけなら union 型リテラル(
type X = "A" | "B")が第一候補。軽くて型安全 - 文字列 enum は実行時オブジェクトを作るが逆引きは無い。文字列で残したい時に
- C# の
[Flags]相当は無い。数値 enum + ビット演算で自前、ToString()の連結も手書き - const enum は軽いが
isolatedModulesで制約あり。配布コードでは避けられることも
「TS の enum は C# の enum と別物」と割り切って、まず union を第一候補にする。これだけで、enum 周りの迷いはだいぶ消えます。
よくある質問
Q1. TypeScript の enum は C# の enum と同じですか?
似ていますが挙動が違います。TS の数値 enum は実行時に JavaScript のオブジェクトを生成し、値から名前を逆引きできます。C# の enum はコンパイル時の定数で、実行時オブジェクトは作りません。TS では多くの場合、union 型リテラルの方が軽くて型安全です。
Q2. enum と union 型はどちらを使うべきですか?
実行時オブジェクトが不要で、値の集合を型で縛りたいだけなら union 型リテラル(type X = "A" | "B")が第一候補です。バンドルサイズが最小で、C# の enum 相当の型安全も得られます。値から名前の逆引きが必要な場合だけ、数値 enum を検討します。
Q3. C# の [Flags] enum は TypeScript にありますか?
[Flags] 属性に相当する仕組みはありません。数値 enum とビット演算(1 << 0 など)で自前で組みます。C# のように ToString() が自動でフラグ名を連結する挙動は無いので、名前で出したい部分は手書きになります。
Q4. const enum は普通の enum と何が違いますか?
const enum はコンパイル時に値へインライン展開され、実行時オブジェクトを生成しません。バンドルが軽くなりますが、isolatedModules 環境(Babel や一部のバンドラ)では制約があるため、ライブラリ配布では避けられることもあります。
次に読むべき記事





以上!
同じところで詰まった人いたら、どんどんシェア待ってるぜ!!
執筆者
バイブス父さん — 業務 SE 7 年(正社員 2 / フリーランス 5)。現職は SEO 直轄部の AI アドバイザー兼 PL、副業で中小 SIer の CTO。SES 複数社・フリーランスエージェント複数経由の経験ベースで「業務 SE 視点」の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524(日々の現場メモ更新中)
📝 About Me で経歴詳細を見る




