TypeScript course menu
TypeScript Enums: Numeric and String Enums, and When to Use Alternatives
Learn TypeScript enums: numeric enums and auto-increment, string enums, using enums in code, reverse mapping, const enums, why many teams prefer literal unions or as const objects, and how enums behave with Node.js type stripping.
On this page
An enum defines a set of named constants. Enums are one of the few TypeScript features that is not just types: they produce real JavaScript objects at runtime.
Numeric enums#
enum Direction {
Up,
Down,
Left,
Right,
}
function move(direction: Direction): string {
return `Moving ${Direction[direction]} (value ${direction})`;
}
console.log(move(Direction.Left));
console.log(Direction.Up, Direction.Right);Moving Left (value 2)
0 3- Members get numbers automatically from 0 (
Up = 0,Down = 1, ...). You can set a starting value:Up = 1. - Numeric enums have a reverse mapping:
Direction[2]is"Left".
String enums#
String enums give each member an explicit string value, which is more readable in logs and data:
enum OrderStatus {
Placed = "PLACED",
Shipped = "SHIPPED",
Delivered = "DELIVERED",
}
function nextStatus(status: OrderStatus): OrderStatus | null {
switch (status) {
case OrderStatus.Placed:
return OrderStatus.Shipped;
case OrderStatus.Shipped:
return OrderStatus.Delivered;
case OrderStatus.Delivered:
return null;
}
}
console.log(nextStatus(OrderStatus.Placed));
console.log(Object.values(OrderStatus));SHIPPED
[ 'PLACED', 'SHIPPED', 'DELIVERED' ]String enums have no reverse mapping.
What an enum compiles to#
Because enums exist at runtime, the compiler generates code for them. The string enum above becomes roughly:
var OrderStatus;
(function (OrderStatus) {
OrderStatus["Placed"] = "PLACED";
OrderStatus["Shipped"] = "SHIPPED";
OrderStatus["Delivered"] = "DELIVERED";
})(OrderStatus || (OrderStatus = {}));This matters because tools that strip types without compiling (such as running .ts files directly in Node.js) cannot handle enums, since they would need to generate this code. TypeScript's erasableSyntaxOnly compiler option reports enums and similar features for exactly this reason.
const enum declarations are inlined (replaced by their values) and generate no object, but they have their own limitations with some build tools.
Enums are nominal#
String enums only accept their own members, not equal strings:
enum OrderStatus {
Placed = "PLACED",
Shipped = "SHIPPED",
}
const s: OrderStatus = "PLACED";error TS2322: Type '"PLACED"' is not assignable to type 'OrderStatus'.This is sometimes useful (it forces use of the named constant) but awkward when values come from JSON or an API as plain strings.
Alternatives: literal unions and as const#
Many TypeScript teams prefer simpler alternatives that are pure types or plain JavaScript:
A union of string literals#
type OrderStatus = "placed" | "shipped" | "delivered";
function label(status: OrderStatus): string {
return status[0].toUpperCase() + status.slice(1);
}
console.log(label("shipped"));ShippedNo runtime code, works with plain strings from APIs, and editors still auto-complete the values (Literal Types).
An as const object#
When you also want named constants and a list of values at runtime:
const OrderStatus = {
Placed: "placed",
Shipped: "shipped",
Delivered: "delivered",
} as const;
type OrderStatus = (typeof OrderStatus)[keyof typeof OrderStatus];
// "placed" | "shipped" | "delivered"
function isDone(status: OrderStatus): boolean {
return status === OrderStatus.Delivered;
}
console.log(isDone(OrderStatus.Shipped), isDone("delivered"), Object.values(OrderStatus));false true [ 'placed', 'shipped', 'delivered' ]This gives OrderStatus.Shipped-style constants like an enum, accepts plain strings, and is ordinary JavaScript. The keyof typeof syntax is explained in keyof, typeof and Indexed Access.
Which to choose?#
Enums are fine and common in existing codebases (and in frameworks such as Angular and NestJS). For new code, literal unions are the simplest choice, and as const objects when you need runtime values.
Practice#
Practice exercise
Rewrite this enum as an as const object with a derived Priority type, and write isUrgent(p: Priority): boolean that is true only for "high":
enum Priority { Low = "low", Medium = "medium", High = "high" }Show solution
const Priority = {
Low: "low",
Medium: "medium",
High: "high",
} as const;
type Priority = (typeof Priority)[keyof typeof Priority];
function isUrgent(p: Priority): boolean {
return p === Priority.High;
}
console.log(isUrgent(Priority.Low), isUrgent("high"));false trueKey takeaway#
Finished this lesson?