TypeScript Enum & Union Generator

Transform raw member lists into modern, production-tested TypeScript enumerations. Effortlessly toggle between traditional string enums, tree-shakeable as const objects, numeric enums, and string literal unions with automatic type guard helper generation.

100% Free & Client-SideModern 'as const' PatternString & Numeric EnumsType Guard Helper OutputZero Dependencies
HiFi ToolKit Logo
Sample Presets:
Enum Configuration
5 distinct member keys detected.
// Modern 'as const' Pattern (Zero Runtime Bundler Bloat)
export const OrderStatus = {
  Pending: 'PENDING',
  Processing: 'PROCESSING',
  Shipped: 'SHIPPED',
  Delivered: 'DELIVERED',
  Cancelled: 'CANCELLED',
} as const;

export type OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];

// Type Guard Helper
export function isOrderStatus(val: unknown): val is OrderStatus {
  return Object.values(OrderStatus).includes(val as OrderStatus);
}

The Complete Technical Guide to TypeScript Enums and Modern Alternatives

Explore why TypeScript enums remain widely misunderstood, the hidden bundler penalties of legacy enums, and why the as const object pattern has become the new industry gold standard.

The TypeScript Enum Controversy Explained

In the vast majority of cases, TypeScript adheres to a strict design principle:types are erased at compile time. Whether you write an interface, a generic constraint, or an intersection type, the compiled JavaScript contains zero runtime trace of that code.

Enums are the rare exception to this rule. When you write a TypeScript enum, the TypeScript compiler emits an actual JavaScript IIFE (Immediately Invoked Function Expression) that creates a real object in memory. While this gives you runtime accessibility, it introduces unexpected side effects:

  • Tree-Shaking Failures: Because enums emit IIFEs with closures, bundlers like Rollup, Webpack, and esbuild often cannot safely tree-shake unused enum members, leaving dead code in production bundles.
  • Reverse-Mapping Confusions: Numeric enums generate two-way mappings (e.g. Status[0] === 'Pending' and Status['Pending'] === 0), creating odd iteration behavior when using Object.keys().
  • Nominal vs Structural Typing: TypeScript is a structurally typed language, but enums behave nominally, meaning passing a raw string 'ACTIVE' to a function expecting Status.Active will fail type checking unless explicitly cast.

The 4 Ways to Represent Enums in TypeScript

1. The 'as const' Object (Recommended)

Uses a standard JavaScript object marked with a const assertion, followed by an extracted union type. It guarantees 100% tree-shaking and zero compilation surprises:

export const UserRole = {
  Admin: 'ADMIN',
  Member: 'MEMBER'
} as const;

export type UserRole = (typeof UserRole)[keyof typeof UserRole];

2. String Literal Union

The purest TypeScript abstraction with literally zero runtime footprint:

export type UserRole = 'ADMIN' | 'MEMBER';

/* No object import required when calling functions */
function setRole(role: UserRole) { ... }
setRole('ADMIN'); /* Passes cleanly! */

3. Traditional String Enum

The classic TypeScript syntax. Values must be referenced through the imported enum identifier:

export enum UserRole {
  Admin = 'ADMIN',
  Member = 'MEMBER'
}

/* Must import UserRole and write UserRole.Admin */

4. Numeric Enum

Auto-assigns incrementing numbers (0, 1, 2...) starting from zero. Emits reverse-mapping keys:

export enum Direction {
  Up = 0,
  Down = 1,
  Left = 2,
  Right = 3
}

Technical Comparison: Enum Paradigms in Modern TypeScript

CriteriaObject as constString Literal UnionStandard String EnumNumeric Enum
Runtime JS EmittedClean plain objectZero (100% erased)IIFE Function ObjectIIFE Function Object
Tree-ShakeableYes (100% safe)Yes (No code to shake)PoorPoor
Raw String CompatibilityAccepts raw stringsAccepts raw stringsRequires Enum importRequires Enum import
Reverse MappingNo (clean iteration)N/ANoYes (confusing keys)
Industry ConsensusModern StandardLightweight StandardLegacy / Prisma modelsLegacy

Validating Unknown Values with Runtime Type Guards

When reading input from an external API or URL query parameter (where the type is initially unknown), a Type Guard safely narrows the type down to your enum:

export function isUserRole(value: unknown): value is UserRole {
  return Object.values(UserRole).includes(value as UserRole);
}

// In your application logic:
const param = router.query.role;
if (isUserRole(param)) {
  // param is strictly typed as UserRole inside this block!
}

Frequently Asked Questions (FAQs)

Unlike nearly all other TypeScript features (which are 100% erased at compile time), enums emit an actual IIFE JavaScript object in compiled output. Numeric enums also create double reverse-mappings (mapping both names to values and numbers back to names), which hampers bundler tree-shaking and bloats production bundle sizes.

The 'as const' pattern uses a standard JavaScript object marked with const assertions: const Status = { Pending: 'PENDING' } as const; type Status = (typeof Status)[keyof typeof Status];. This achieves 100% type safety, zero bundler bloat, perfect tree-shaking, and seamless JSON serialization without compiler surprises.

A const enum (const enum Status { ... }) inlines member values directly at call sites during compilation, leaving no runtime object in JavaScript. However, const enums fail when using isolated module transpilation (such as Babel, Vite, esbuild, or Next.js with isolatedModules: true), making standard enums or 'as const' objects preferable.

A string literal union (type Status = 'PENDING' | 'ACTIVE') has zero runtime overhead and requires no import statement when passing raw strings. Enums require importing the enum object everywhere you want to reference a member (Status.Active).

Using an 'as const' object, you can validate runtime values with Object.values(Status).includes(val as Status). This proves to the TypeScript compiler that the value safely belongs to the union type.

Yes. With 'as const' objects, you can use Object.keys(Status), Object.values(Status), or Object.entries(Status). For string enums, Object.values(Status) returns all enum string values.

Prisma and database ORMs generate String Enums by default (e.g. enum Role { USER = 'USER' }). In application code, converting them to or matching them with 'as const' objects ensures seamless compatibility.

Yes, 100%. All parsing, formatting, and generation occurs entirely on your device via client-side JavaScript. No data is ever transmitted across the network.