TS

Enums

TypeScript · 6 entries

Numeric Enums

syntax
enum Name {
  Member = value,
  AutoIncremented,
}
example
enum HttpStatus {
  Ok = 200,
  Created = 201,
  BadRequest = 400,
  Unauthorized = 401,
  NotFound = 404,
  ServerError = 500,
}

function isSuccessful(status: HttpStatus): boolean {
  return status >= 200 && status < 300;
}

console.log(isSuccessful(HttpStatus.Ok)); // true
console.log(HttpStatus.NotFound);         // 404
output
// true
// 404

Note Numeric enums auto-increment from the last explicit value. If no values are assigned, they start at 0. Beware: TypeScript allows assigning ANY number to a numeric enum variable, not just declared members — this is a known type safety gap.

String Enums

syntax
enum Name {
  Member = "value",
}
example
enum LogLevel {
  Debug = "DEBUG",
  Info = "INFO",
  Warn = "WARN",
  Error = "ERROR",
  Fatal = "FATAL",
}

function log(level: LogLevel, message: string) {
  console.log(`[${level}] ${message}`);
}

log(LogLevel.Error, "Database connection lost");
// log("ERROR", "..."); // Error: '"ERROR"' is not assignable to 'LogLevel'
output
// [ERROR] Database connection lost

Note String enums do NOT auto-increment — every member needs an explicit string value. They are more type-safe than numeric enums because you cannot accidentally pass an arbitrary string. The downside: you cannot pass the raw string value, only the enum member.

const Enums

syntax
const enum Name {
  Member = value,
}
example
const enum Direction {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT",
}

const playerDirection = Direction.Up;
// Compiles to: const playerDirection = "UP";
// No Direction object exists at runtime
output
// const enums are completely erased — values are inlined at compile time

Note const enums produce zero runtime JavaScript — member accesses are replaced with literal values. However, they have compatibility issues: they cannot be used with --isolatedModules (Babel, esbuild, SWC), and they do not work across declaration files in some setups. Many teams ban them in favor of plain unions.

Enum Members as Types

syntax
function fn(param: EnumName.Member): void { ... }
example
enum Permission {
  Read = "READ",
  Write = "WRITE",
  Admin = "ADMIN",
}

// Each member is its own type
function grantAdmin(userId: string, level: Permission.Admin) {
  console.log(`Granting admin to ${userId}`);
}

grantAdmin("usr_1", Permission.Admin); // OK
// grantAdmin("usr_1", Permission.Read); // Error: not assignable to Permission.Admin
output
// Individual enum members can be used as specific types

Note Each enum member is a subtype of the enum. You can use individual members in type positions to restrict parameters to a specific enum value. This is more useful with string enums since numeric enum members have the wider number type.

Reverse Mapping (Numeric Enums)

syntax
EnumName[numericValue] // returns the member name as string
example
enum Priority {
  Low = 0,
  Medium = 1,
  High = 2,
  Critical = 3,
}

const level = Priority.High;
console.log(level);             // 2
console.log(Priority[level]);   // "High"

// Practical: converting API response to label
function getPriorityLabel(code: number): string {
  return Priority[code] ?? "Unknown";
}
output
// 2
// "High"

Note Reverse mapping ONLY works with numeric enums — string enums do not generate reverse mappings. The compiled JavaScript includes both name→value and value→name entries in the enum object. const enums do not support reverse mapping since they have no runtime object.

Union Types as Enum Alternatives

syntax
type Name = "value1" | "value2" | "value3";
example
// Instead of enum:
type Theme = "light" | "dark" | "system";

// Object const pattern for when you need both runtime values and types
const THEMES = {
  Light: "light",
  Dark: "dark",
  System: "system",
} as const;

type Theme2 = (typeof THEMES)[keyof typeof THEMES];
// "light" | "dark" | "system"

function applyTheme(theme: Theme2) {
  document.body.dataset.theme = theme;
}
output
// Union types achieve the same result without enum's quirks

Note Many TypeScript teams prefer string literal unions over enums for simplicity, full tree-shaking, and compatibility with --isolatedModules. The 'as const' object pattern gives you both runtime values (for iteration/lookup) and a derived type — the best of both worlds.