TypeScript

TypeScript Enums👨‍💻

Enums define a closed set of named constants — think HTTP status codes, user roles, or order states. TypeScript gives you three ways to model them: traditional enums, const enum, and the increasingly popular as const object pattern. Each has real trade-offs around bundle size, type safety, and developer ergonomics, and picking the wrong one can bite you in production.

Key Takeaways

  • 1String enums are almost always preferable to numeric enums — the values are self-documenting and don't silently break when you reorder members
  • 2Numeric enums support reverse mapping (`Status[0]` gives you the name), but this generates extra runtime code most teams never use
  • 3`const enum` inlines values at compile time and produces zero runtime JavaScript, but breaks if consumers use `--isolatedModules` or Babel
  • 4The `as const` object pattern is the modern alternative: no extra runtime code, works with all toolchains, and values are plain strings you can use directly
  • 5Enums create their own type namespace — you can't pass a raw string where an enum is expected, even if the value matches. This is either a feature or an annoyance depending on how strict you want your API boundaries
  • 6Union types (`type Status = 'active' | 'inactive'`) are the simplest approach when you don't need a runtime object to iterate over

Master typescript enums

Take the TypeScript Essentials course with hands-on lessons and challenges.

Examples

String enum — HTTP status codes

typescript

Numeric enums make sense for HTTP status codes because the numeric values carry real meaning — you might compare against ranges (>= 500) or send them over the wire. This is one of the few cases where numeric enums are the right call.

String enum — order lifecycle

typescript

String enums shine for domain values that cross API boundaries. The SCREAMING_CASE values match what your backend sends, and the enum type prevents you from passing arbitrary strings into business logic functions.

as const — the modern alternative

typescript

The as const pattern produces a plain object with literal types. Unlike enums, you can pass either the constant reference (LogLevel.Warn) or the raw string ("error") — both are valid. This flexibility is why many teams prefer it.

const enum — zero runtime cost

typescript

const enum inlines every reference at compile time — Direction.Left becomes the string "LEFT" directly. No runtime object is created. The catch: this only works with TypeScript's own compiler. Babel, SWC, and esbuild either ignore const enum or need special configuration.

Union type — when you don't need a runtime object

typescript

For small, stable sets of values, a plain union type is the simplest solution. You get autocomplete, exhaustiveness checking in switch statements, and zero runtime overhead. No import needed — just use the string directly.

Discriminated union with enum — API error handling

typescript

Enums pair well with discriminated unions. The enum value acts as the discriminant, and TypeScript narrows the type in each switch branch. This pattern is common in error handling, event systems, and state machines.

Common Mistakes

Mistake:

Using numeric enums for values that cross API boundaries — your backend sends `"PENDING"` but your enum member has value `0`

Fix:

Use string enums or `as const` when values are serialized to JSON. Numeric enums only make sense when the number itself has meaning (HTTP status codes, priority levels with numeric comparisons).

Mistake:

Passing a raw string where an enum type is expected — `updateStatus("SHIPPED")` fails even though `OrderStatus.Shipped === "SHIPPED"`

Fix:

Enums are nominally typed: you must use the enum member (`OrderStatus.Shipped`), not the raw string. If you want to accept both, use `as const` objects instead, which produce a union of string literals.

Mistake:

Using `const enum` in a library published to npm — consumers using Babel or `--isolatedModules` will get errors because the enum doesn't exist at runtime

Fix:

Avoid `const enum` in any code that will be consumed by other packages. Use regular string enums or `as const` objects instead. The TypeScript team themselves recommend against `const enum` in library code.

Mistake:

Relying on numeric enum reverse mapping as a validation mechanism — `if (MyEnum[value] !== undefined)` accepts any number, not just valid members

Fix:

Numeric enums map every number to `undefined` for non-members but also create reverse mappings for valid ones. For validation, use `Object.values(MyEnum).includes(value)` or switch to string enums where the value space is constrained.

Best Practices

  • Default to string enums over numeric enums — string values are readable in logs, debuggers, and JSON payloads without needing a reverse lookup
  • Consider `as const` objects for new code — they produce cleaner JavaScript output, work with every bundler and transpiler, and let you use raw string values interchangeably with the constant references
  • Use plain union types (`type Role = 'admin' | 'editor'`) when the set is small and you don't need a runtime object to iterate over or validate against
  • Pair enums with discriminated unions for complex domain modeling — the enum member as a discriminant gives you exhaustive type narrowing in switch statements
  • Never mix string and numeric members in the same enum — TypeScript allows it, but it creates confusing behavior and makes the type harder to reason about

Summary

TypeScript gives you four ways to model a fixed set of values: numeric enums, string enums, `const enum`, and `as const` objects. String enums are the safe default for most domain modeling. `as const` objects are the modern choice when you want flexibility and clean JavaScript output. Numeric enums are reserved for cases where the number matters (like HTTP status codes). And plain union types are the simplest option when you just need a type constraint without a runtime object. Pick based on whether you need runtime iteration, cross-boundary serialization, or just compile-time checking.

Practice TypeScript with hands-on challenges

Learn typescript enums hands-on in your IDE

Interactive lessons and challenges on Stanza, practice in VS Code, Cursor, or the web.

Related Concepts

Related Cheatsheets

Master TypeScript with Stanza

Interactive lessons and challenges, right in your code editor.

Check the free courses. No credit card.