TypeScript Enums and Their Alternatives

An enum gives a set of named constants one type. Numeric enums count up from 0 and also map numbers back to names; string enums need a literal per member and have no reverse mapping; const enums are inlined and vanish. Unlike most TypeScript, regular enums emit real JavaScript, so many teams prefer as const objects.

Five ways to model a fixed set of values
StyleEmits JavaScript?Reverse mappingAccepts a raw literal?
Numeric enumYes, an objectYes, number to nameNumbers that match a member
String enumYes, an objectNoNo, must use Enum.Member
const enumNo, values inlinedNoAs its enum kind
as const objectYes, a plain objectNo (write one if needed)Yes
Union of literalsNo, erasedNoYes

Updated

Type member names into the generator and switch between styles. It writes the TypeScript declaration and the JavaScript the compiler would output, so you can see the cost of each choice before it lands in your bundle.

Enum generator

enum Status { Draft, Review, Live }
var Status;
(function (Status) {
    Status[Status["Draft"] = 0] = "Draft";
    Status[Status["Review"] = 1] = "Review";
    Status[Status["Live"] = 2] = "Live";
})(Status || (Status = {}));

The three enum kinds and what they emit

Numeric enum with a starting value

Mid and High continue counting from Low. The output object maps both ways.

enum Level { Low = 1, Mid, High }

// tsc output
var Level;
(function (Level) {
    Level[Level["Low"] = 1] = "Low";
    Level[Level["Mid"] = 2] = "Mid";
    Level[Level["High"] = 3] = "High";
})(Level || (Level = {}));

String enum

Each member is assigned once, with no reverse entry.

enum Theme { Light = "light", Dark = "dark" }

// tsc output
var Theme;
(function (Theme) {
    Theme["Light"] = "light";
    Theme["Dark"] = "dark";
})(Theme || (Theme = {}));

const enum

The declaration disappears and each use becomes its value.

const enum Size { Small, Large }
const s = Size.Large;

// tsc output
const s = 1 /* Size.Large */;

Erasable alternatives

as const object plus a derived union

Works with type strippers, iterates with Object.values, and accepts the raw string.

const Role = {
  Admin: "admin",
  Editor: "editor",
  Viewer: "viewer",
} as const;

type Role = (typeof Role)[keyof typeof Role]; // "admin" | "editor" | "viewer"

function canPublish(role: Role) {
  return role !== Role.Viewer;
}

canPublish("editor");      // fine
canPublish(Role.Admin);    // fine
Object.values(Role);       // ["admin", "editor", "viewer"]

Plain union when no runtime list is needed

Erased entirely; the compiler still rejects any other string.

type Size = "sm" | "md" | "lg";

function padding(size: Size) {
  return size === "sm" ? 4 : size === "md" ? 8 : 12;
}

Forbid enums in a type-stripped project

With this option, an enum declaration is a compile error.

{
  "compilerOptions": {
    "erasableSyntaxOnly": true,
    "verbatimModuleSyntax": true
  }
}

Numeric enums: compact but fragile

Without initialisers, members are numbered 0, 1, 2 in order. Give the first one a value, such as Low = 1, and the rest continue from there. The emitted object holds both name to number and number to name.

That compactness is also the risk. The numbers mean nothing outside your code, and adding a member in the middle silently renumbers everything after it.

String enums: readable and strict

Every member needs a string literal, which makes values self-describing in logs, URLs and JSON. The strictness cuts both ways: a value read from an API is a string, not the enum, so you still need a check before treating it as one.

The as const pattern

An object literal marked as const keeps its exact string values as types. Pair it with type Role = (typeof Role)[keyof typeof Role] and you get a runtime object for iteration plus a union type that accepts both Role.Admin and the literal admin.

This is plain JavaScript with annotations, so every type stripper handles it, and nothing extra is generated beyond the object you wrote.

How it works

  1. Enter member names separated by commas, for example Draft, Review, Live.
  2. For numeric enums, set the starting number; later members count up from it.
  3. Pick a style to see its declaration, its emitted JavaScript and how you use it.
  4. Copy the version that suits your project.

Tips

Frequently asked questions

What is a reverse mapping?

For numeric enums tsc stores both directions on the object, so Direction.Up is 0 and Direction[0] is the string Up. String members get no reverse entry, because the value could collide with another member's name.

Why can't I pass a plain string to a function that takes a string enum?

String enum members are distinct types. The compiler reports that the literal is not assignable to the enum, so callers must write Status.Draft. If you want callers to pass the literal, use a union of string literals instead.

How do I loop over enum values?

For a string enum, Object.values(MyEnum) returns the values. For a numeric enum the object also holds the reverse keys, so filter: Object.values(MyEnum).filter((v) => typeof v === 'number').

Are enums deprecated?

No. They remain part of TypeScript. The newer erasableSyntaxOnly option, added in TypeScript 5.8, simply lets projects that rely on type stripping forbid them, because an enum cannot be removed by deleting annotations.

Does Node.js run files with enums directly?

Not with default type stripping. Node's documentation lists enum declarations among the features that need code generation and are not supported in that mode, so compile them with tsc or another transformer, or switch to an as const object.

Related: TypeScript vs JavaScript, TypeScript interfaces, React with TypeScript, JSON to TypeScript converter.

Start for free or see all free tools. We.Inc is free to start, with no card needed. AI building is on paid plans from $20 a month.

More in Guides

We.Inc is an AI-powered website builder you can resell under your own brand. Launch a branded client dashboard, bill on Stripe Connect, and deliver AI-generated websites in minutes. White-label plans start at $99 a month for 25 client sites, with a 7-day free trial and no per-site fees.

Product

Who It's For

Features

Resources

Company

View Sitemap