TypeScript Enum & Union Generator

Easily generate and convert between String Enums, Numeric Enums, modern 'as const' Objects, and clean String Literal Unions. Includes instant generation of runtime Type Guards and iteration helpers.

100% Free & Client-SideModern 'as const' PatternRuntime Type Guard GeneratorTree-Shakeable SyntaxZero 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 Guide to Enums, Unions & Constants in TypeScript

Explore the architectural differences, emitted JavaScript code, tree-shaking behavior, and modern industry best practices for representing discrete sets of constants.

The Great TypeScript Enum Debate: History & Architecture

TypeScript was designed from its inception around a strict philosophical principle: pure type erasure. When you compile TypeScript using tsc, type annotations, interfaces, and type aliases disappear completely, leaving clean, idiomatic JavaScript behind.

However, Enums (introduced in early TypeScript 0.9 before ECMAScript 2015 introduced block scoping and const declarations) are one of the rare language constructs that violate this principle. Declaring a TypeScript enum causes the compiler to emit an actual runtime JavaScript object wrapped inside an Immediately Invoked Function Expression (IIFE). Over the past decade, this historical anomaly has generated significant architectural debate within the web development community.

Emitted JavaScript: What Really Runs in the Browser

To understand why modern frontend bundlers (such as Webpack, Rollup, Vite, and Turbopack) struggle with enums, examine the compiled output:

Standard Numeric Enum

TypeScript Input:

enum Direction {
  Up,
  Down
}

Compiled JavaScript Output:

var Direction;
(function (Direction) {
    Direction[Direction["Up"] = 0] = "Up";
    Direction[Direction["Down"] = 1] = "Down";
})(Direction || (Direction = {}));

Modern 'as const' Object Pattern

TypeScript Input:

const Direction = {
  Up: 'UP',
  Down: 'DOWN'
} as const;
type Direction = typeof Direction[keyof typeof Direction];

Compiled JavaScript Output:

const Direction = {
  Up: 'UP',
  Down: 'DOWN'
};

The Numeric Enum Trap: Bidirectional Reverse Mappings

Notice the strange assignment in the compiled numeric enum: Direction[Direction["Up"] = 0] = "Up". This creates a two-way bidirectional mapping where keys map to numbers AND numbers map back to string keys:

console.log(Direction.Up);   // 0
console.log(Direction[0]);   // "Up"
console.log(Object.keys(Direction)); // ["0", "1", "Up", "Down"] <-- Contains duplicate keys!

If a frontend developer loops over Object.keys(Direction) to populate a dropdown select menu in React or Vue, the user interface renders four entries instead of two, displaying confusing numbers alongside names!

Architectural Matrix: Comparing TypeScript Enum Alternatives

Evaluation CriteriaObject as const ⭐String 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)
JSON SerializationFlawless & naturalFlawless & naturalRequires enum checksSerializes to numbers
Industry ConsensusModern StandardLightweight StandardLegacy / Prisma modelsDiscouraged

Const Enums & The isolatedModules Build Pitfall

To avoid emitting runtime objects, TypeScript introduced const enum Status { ... }. The compiler inlines raw literal values wherever the enum is referenced, omitting any JavaScript object in production output.

However, modern build pipelines (such as Babel, SWC, Vite, esbuild, and Next.js) transpile files individually in parallel worker threads without reading ambient type definitions across other files. When running with"isolatedModules": true in tsconfig.json, single-file compilers cannot resolve inlined values from imported const enum files, throwing breaking build errors. For this reason, the TypeScript core team advises against const enum in multi-package or modern bundler setups.

Validating External Inputs with Runtime Type Guards & Zod

When receiving payload data from external REST APIs or query strings, values arrive as untyped unknown strings. A Type Guard safely verifies membership at runtime:

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

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

export function isUserRole(value: unknown): value is UserRole {
  return typeof value === 'string' && Object.values(UserRole).includes(value as UserRole);
}

Similarly, when validating request bodies using runtime schema validators like Zod, the 'as const' pattern pairs seamlessly with z.nativeEnum(UserRole), ensuring bulletproof input validation in Next.js Server Actions and Express endpoints.

Bitwise Flag Enums & Bitmasking in Systems Architecture

Numeric enums retain one legitimate, powerful niche: Bitwise Flag Enums. By assigning powers of two (using bit shift notation 1 << n), a single integer variable can represent multiple simultaneous states through bitwise bitmasking:

enum FilePermission {
  None    = 0,
  Read    = 1 << 0, // 1  (0001)
  Write   = 1 << 1, // 2  (0010)
  Execute = 1 << 2  // 4  (0100)
}

// Combining permissions using bitwise OR:
const userPerms = FilePermission.Read | FilePermission.Write; // 3 (0011)

// Testing permissions using bitwise AND:
const canWrite = (userPerms & FilePermission.Write) !== 0; // true
const canExec  = (userPerms & FilePermission.Execute) !== 0; // false

This technique is ubiquitous in game development engines, canvas graphics libraries, and low-level operating system device driver interfaces where packing multiple boolean flags into a single 32-bit integer optimizes memory bandwidth.

Compile-Time Exhaustive Pattern Matching with the never Type

A key advantage of strict typing is ensuring that when a new member is added to an enum or union (e.g., adding ARCHIVEDto Status), every switch statement across your enterprise codebase is immediately flagged by the compiler if the new case is not handled:

function handleStatus(status: Status): string {
  switch (status) {
    case Status.Pending:
      return 'Waiting for review';
    case Status.Active:
      return 'Account is active';
    case Status.Suspended:
      return 'Account is suspended';
    default: {
      // If any enum variant is unhandled, TypeScript produces a compile-time error here!
      const _exhaustiveCheck: never = status;
      throw new Error(`Unhandled status case: ${_exhaustiveCheck}`);
    }
  }
}

Assigning the unhandled state to the bottom type never guarantees that no developer can forget to update critical business logic pipelines when system requirements evolve.

Enterprise ORM Mapping & Internationalization (i18n)

Modern backend architectures frequently sync TypeScript constant definitions with database schemas:

Prisma & PostgreSQL Enum Alignment

PostgreSQL supports native database enums created via CREATE TYPE order_status AS ENUM (...). Prisma ORM generates matching TypeScript string enums in its generated client code. In consumer frontend codebases, wrapping Prisma enums in 'as const' object helpers guarantees type compatibility without dragging backend client dependencies into browser bundles.

UI Translation & Label Dictionaries

Enums should never be displayed directly to end users in raw uppercase form. Instead, use TypeScript's Record<Status, string> to create exhaustive localization dictionaries that map internal machine states to localized, human-friendly translation keys for English, Spanish, German, or Japanese view models.

JSON Serialization Pitfalls: Numbers vs. Strings

When data is sent over the wire in REST APIs or stored in document databases (such as MongoDB or DynamoDB), numeric enums serialize to raw numbers (e.g., "role": 0). If a backend developer later reorders the enum members (e.g., inserting SuperAdmin before Admin), all historical database records become instantly corrupted because the numeric indices shift!

In contrast, string literal unions and 'as const' string objects serialize directly to meaningful string values (e.g., "role": "ADMIN"). String serialization is resilient to member reordering, drastically simplifies database query debugging, and integrates cleanly with external microservices and third-party consumers.

How to Use the HiFi ToolKit TypeScript Enum & Union Generator

  1. Specify Enum Name: Enter your PascalCase entity name (e.g., UserRole, OrderStatus, or ThemeMode).
  2. Enter Members: Provide your comma-separated or newline-separated list of member names.
  3. Choose Output Flavor: Toggle between Modern 'as const' Object, Clean String Literal Union, Standard String Enum, or Numeric Enum.
  4. Copy Generated Code: Copy the formatted TypeScript declarations and runtime type guards with a single click.

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, alongside a type union derived via typeof Status with keyof typeof Status. This achieves 100% type safety, zero bundler bloat, perfect tree-shaking, and seamless JSON serialization without compiler surprises.

A const enum 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 enabled), 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.

100% Client-Side Privacy Guarantee

Your proprietary code architecture, entity names, and constant definitions are completely protected. The HiFi ToolKit TypeScript Enum & Union Generator operates 100% within your client browser using local JavaScript. No code snippets, enum definitions, or enterprise data structures are ever uploaded to cloud servers or logged in analytics services.

Explore Related Tools

Hand-picked utilities and calculators related to this tool.

TypeScript Tools