ZetCode

TypeScript Enums

last modified October 15, 2026

An enum defines a set of named constants, such as the days of the week, the states of an order, or log levels. Instead of scattering magic numbers or strings through the code, the program refers to Status.Shipped or LogLevel.Error. Enums are one of the few TypeScript features that are not just types: an enum declaration compiles to a JavaScript object that exists at runtime.

Defining Enums

An enum is declared with the enum keyword. By default, its members get numbers starting from 0.

defining_enums.ts
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

console.log(Direction.Up, Direction.Down, Direction.Left, Direction.Right);
console.log(typeof Direction.Up);

The members of a numeric enum are numbers: Up is 0, Down is 1, and so on. The code reads better with Direction.Left than with a bare 2, and the compiler checks that the name exists.

$ npx tsx defining_enums.ts
0 1 2 3
number

Explicit Values

A member can be given a value. The following members continue counting from it.

enum_members.ts
enum HttpStatus {
    Ok = 200,
    Created,
    NoContent = 204,
    NotFound = 404,
    ServerError = 500,
}

console.log(HttpStatus.Ok, HttpStatus.Created, HttpStatus.NoContent);
console.log(HttpStatus.NotFound, HttpStatus.ServerError);

Created follows Ok and gets 201 automatically. The other members have explicit values. Explicit values are recommended when the numbers have a meaning outside the program, as HTTP status codes do, or are stored in a database: adding a member in the middle of an enum without values would shift all numbers after it.

$ npx tsx enum_members.ts
200 201 204
404 500

String Enums

Members of a string enum must all be initialized with string literals.

string_enums.ts
enum LogLevel {
    Debug = "DEBUG",
    Info = "INFO",
    Error = "ERROR",
}

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

log(LogLevel.Info, "server started");
log(LogLevel.Error, "connection lost");

The values of a string enum are readable when printed, logged, or sent over the network, unlike the numbers of a numeric enum. They do not auto-increment, so every member needs a value.

$ npx tsx string_enums.ts
[INFO] server started
[ERROR] connection lost

Enums as Types

An enum is also a type. A parameter of an enum type accepts only members of that enum.

enum_types.ts
enum OrderStatus {
    Pending = "PENDING",
    Shipped = "SHIPPED",
    Delivered = "DELIVERED",
}

function describe(status: OrderStatus): string {
    switch (status) {
        case OrderStatus.Pending:
            return "waiting for payment";
        case OrderStatus.Shipped:
            return "on the way";
        case OrderStatus.Delivered:
            return "delivered";
    }
}

let current: OrderStatus = OrderStatus.Pending;
console.log(describe(current));

current = OrderStatus.Shipped;
console.log(describe(current));

Because every member is handled in the switch, the compiler knows that describe always returns a string. If a new status were added to the enum, the function would no longer compile until the new case is handled.

$ npx tsx enum_types.ts
waiting for payment
on the way
enum_type_error.ts
enum OrderStatus {
    Pending = "PENDING",
    Shipped = "SHIPPED",
}

function ship(status: OrderStatus): void {}

ship(OrderStatus.Shipped);
ship("SHIPPED");

A string enum does not accept a plain string, even one equal to the value of a member. This keeps the code consistent, but it also means that data from JSON or a form must be converted to the enum type first.

$ npx tsc --noEmit
enum_type_error.ts(9,6): error TS2345: Argument of type '"SHIPPED"' is not assignable to parameter of type 'OrderStatus'.

Reverse Mapping

Numeric enums also map values back to member names.

enum_constants.ts
enum Colors {
    Red,
    Green,
    Blue,
}

console.log(Colors[0], Colors[1], Colors[2]);
console.log(Colors[Colors.Green]);
console.log(Colors);

Colors[0] looks up the name of the member whose value is 0. The number is the member's value, not a position. Printing the whole enum shows how this works: the generated object contains both directions, from names to values and from values to names. String enums have no reverse mapping.

$ npx tsx enum_constants.ts
Red Green Blue
Green
{ '0': 'Red', '1': 'Green', '2': 'Blue', Red: 0, Green: 1, Blue: 2 }

Enum Flags

Members with power-of-two values can be combined as bit flags.

computed_enum_members.ts
enum Permission {
    None = 0,
    Read = 1 << 0,
    Write = 1 << 1,
    Execute = 1 << 2,
}

const editor = Permission.Read | Permission.Write;

function has(perms: Permission, flag: Permission): boolean {
    return (perms & flag) === flag;
}

console.log(editor);
console.log(has(editor, Permission.Write), has(editor, Permission.Execute));

The shift expressions give the members the values 1, 2, and 4, so each occupies its own bit. The bitwise OR combines Read and Write into 3, and the bitwise AND in has tests whether a flag is set. Such values are compact, for example for permissions stored in a database.

$ npx tsx computed_enum_members.ts
3
true false

Computed Members

Enum members can be initialized with expressions. Constant expressions are evaluated at compile time; other expressions at runtime.

computed_members.ts
enum FileSize {
    KB = 1024,
    MB = KB * 1024,
    GB = MB * 1024,
}

enum Defaults {
    Timeout = 30 * 1000,
    Name = "app".length,
}

console.log(FileSize.KB, FileSize.MB, FileSize.GB);
console.log(Defaults.Timeout, Defaults.Name);

Members can refer to earlier members, as MB and GB do. Defaults.Name is a computed member: its value comes from a call at runtime. Members after a computed member must have an explicit initializer, because the compiler cannot continue counting from an unknown value.

$ npx tsx computed_members.ts
1024 1048576 1073741824
30000 3

Iterating Over Enums

Because enums are objects, their members can be listed at runtime. Numeric enums need care because of the reverse mapping.

iterate_enum.ts
enum Weekday {
    Monday = 1,
    Tuesday,
    Wednesday,
}

enum Size {
    Small = "S",
    Medium = "M",
    Large = "L",
}

console.log(Object.keys(Weekday));

const dayNames = Object.keys(Weekday).filter(key => Number.isNaN(Number(key)));
console.log(dayNames);

console.log(Object.values(Size));

Object.keys on a numeric enum returns both the names and the numeric keys of the reverse mapping. Filtering out the keys that are numbers leaves only the names. A string enum has no reverse mapping, so Object.values returns exactly its values.

$ npx tsx iterate_enum.ts
[ '1', '2', '3', 'Monday', 'Tuesday', 'Wednesday' ]
[ 'Monday', 'Tuesday', 'Wednesday' ]
[ 'S', 'M', 'L' ]

Const Enums

A const enum produces no object at runtime. Its uses are replaced by the literal values during compilation.

const_enum.ts
const enum Unit {
    Pixel = "px",
    Percent = "%",
}

function size(value: number, unit: Unit): string {
    return `${value}${unit}`;
}

console.log(size(16, Unit.Pixel), size(50, Unit.Percent));

The compiled JavaScript contains "px" and "%" directly instead of looking them up in an object. This saves a little code, but const enums do not work with tools that compile files one by one, such as Babel, esbuild, or the isolatedModules option, when the enum is declared in another file. Libraries should not export them.

$ npx tsx const_enum.ts
16px 50%

Alternatives to Enums

Many projects replace enums with literal unions or with objects marked as const, which need no special compilation.

enum_alternatives.ts
type Theme = "light" | "dark";

const Status = {
    Active: "ACTIVE",
    Suspended: "SUSPENDED",
} as const;

type Status = (typeof Status)[keyof typeof Status];

function setTheme(theme: Theme): string {
    return `theme set to ${theme}`;
}

function check(status: Status): string {
    return status === Status.Active ? "can log in" : "access denied";
}

console.log(setTheme("dark"));
console.log(check(Status.Suspended), check("ACTIVE"));
console.log(Object.values(Status));

A literal union like Theme is the simplest choice when the values are self-explanatory. The as const object provides named constants, like an enum, and the derived Status type is "ACTIVE" | "SUSPENDED". Unlike a string enum, it also accepts the plain string "ACTIVE", which is convenient for data from JSON. Both alternatives are plain JavaScript, so they also work in Node.js when types are only stripped, which enums do not.

$ npx tsx enum_alternatives.ts
theme set to dark
access denied can log in
[ 'ACTIVE', 'SUSPENDED' ]

Best Practices

Source

TypeScript Handbook: Enums

This tutorial covered enums in TypeScript. They name a fixed set of related constants; for new code, literal unions and as const objects are worth considering as lighter alternatives.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all TypeScript tutorials.