Skip to content
Theory · Unions and literal types · lesson 7 of 8

Enums

In an office building's elevator, the buttons read "Parking", "Lobby" and "Roof". A person presses a button by its name, while the elevator receives a floor number: 0, 1 or 2. Names are handy for people, numbers for the machine, and the buttons link one to the other.

Programs are full of such sets: difficulty levels, order statuses, user roles. You could write bare numbers 0, 1 and 2 in the code, but a month later nobody will remember what 2 means. For named sets of values TypeScript has the enum, declared with the enum keyword.

TypeScript
enum Level {
    Low,
    Mid,
    High,
}

function describe(level: Level) {
    console.log(level);
}

describe(Level.Mid);  // 1
describe(Level.High); // 2

Let's walk through the example line by line:

  1. enum Level { … } declares an enum with three members: Low, Mid and High.
  2. The members get numbers in order, starting from zero: Low is 0, Mid is 1, High is 2.
  3. level: Level—the enum's name works as a type: the parameter takes members of Level, and tsc rejects describe(7): no member of Level has the value 7. A number equal to a member's value, such as describe(1), passes.
  4. describe(Level.Mid) prints 1, not Mid: a member's value is a number.

Member values

Here the elevator picture ends. The factory chose the button numbers, but you can set the values of enum members yourself—numbers and strings alike.

TypeScript
enum Priority {
    Low = 1,
    Mid,
    High,
}

enum Color {
    Red = "RED",
    Green = "GREEN",
}

console.log(Priority.Mid, Priority.High); // 2 3
console.log(Color.Green);                 // GREEN

Your own numbers

Low = 1 sets the first member's value by hand. A member without a value gets the previous member's value plus one, so Mid is 2 and High is 3. This rule works anywhere in the enum, not only at the start.

String values

Red = "RED" makes the enum a string enum. Such values read well in logs and in data from a server. Counting on from the previous member does not work for strings, so every member gets its value written out—otherwise tsc reports Enum member must have initializer.

An enum stays in JavaScript

tsc erases annotations and type declarations. That does not work for an enum: its values are needed at run time. Here is the start of the .js file tsc writes from the first example—Level has become a real object:

JavaScript
"use strict";
var Level;
(function (Level) {
    Level[Level["Low"] = 0] = "Low";
    Level[Level["Mid"] = 1] = "Mid";
    Level[Level["High"] = 2] = "High";
})(Level || (Level = {}));

Each line inside the function writes a pair both ways: Level.Mid gives 1, and Level[1] gives "Mid". That is how a numeric enum lets you find a member's name by its value.

Interviewers like to ask this: what is left of an enum after compilation? The answer: an object with the members, and for a numeric enum also the reverse "number → name" pairs.

A literal union instead of an enum

Many teams write a union of literal types instead of an enum: type Level = "low" | "mid" | "high";. It is checked as strictly, but it disappears entirely at compile time and adds not a single line to the .js. An enum is the choice when you need names for numbers or an object with all the values at run time.

Common mistake

A file with an enum is run with node levels.ts:

TypeScript
enum Level { // ✗
    Low,
    Mid,
    High,
}

console.log(Level.Mid);
TEXT
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript enum is not supported in strip-only mode

tsc compiles this file without errors. It is Node that refuses: node file.ts runs in a mode that only erases types, while an enum has to become an object. Compile the file with tsc levels.ts and run node levels.js, or try the code in the TypeScript Playground. node file.ts runs a literal union without any trouble.

Key points

  • An enum gives names to a set of values; numbering starts at 0, and a member without a value gets the previous member's value plus one.
  • In a string enum, every member gets its value written out.
  • An enum is not erased: tsc turns it into an object in the .js, and node file.ts will not run such a file.
  • A literal union like "low" | "mid" | "high" is a common replacement for an enum: it disappears entirely at compile time.