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.
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 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.
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 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 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 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.
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.
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.
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 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.
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
- Prefer String Enums: Their values are readable in logs and stable when members are reordered.
- Give Explicit Values: Set the values of numeric enums that are stored or sent elsewhere.
- Handle Every Member: Switch over all members, so that the compiler reports a missing case when the enum grows.
- Avoid const enum in Libraries: They break with tools that compile files independently.
- Consider Alternatives: A literal union or an
as constobject often does the same job without generated code.
Source
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
List all TypeScript tutorials.