Enums in TypeScript: A Developer’s Guide

Updated: January 7, 2024 By: Guest Contributor Post a comment

Introduction

An enumeration, or enum, is a feature in TypeScript that allows us to define a set of named constants. Using enums can make it easier to document intent or create a set of distinct cases. This guide will walk you through the versatility of enums in TypeScript with practical code examples.

Understanding Enums

In TypeScript, an enum is a way of giving more friendly names to sets of numeric values. Define an enum using the enum keyword:

enum Direction {
  Up,
  Down,
  Left,
  Right
}

By default, enums begin numbering their members starting at 0. You can access the values using the dot notation:

let go: Direction = Direction.Up;

Enums can be assigned numbers directly, providing a custom numeric value for each member.

enum Status {
  NotStarted = 1,
  InProgress,
  Completed
}
console.log(Status.InProgress); // Outputs 2

String Enums

In TypeScript, enums can also be strings. Here’s how you define enums with string values:

enum PrintMedia {
  Newspaper = 'NEWSPAPER',
  Newsletter = 'NEWSLETTER',
  Magazine = 'MAGAZINE',
  Book = 'BOOK'
}

String enums offer the benefit of runtime introspection and more readable values.

Heterogeneous Enums

Enums can also contain both string and number values. These heterogeneous enums are not as commonly used but are legal in TypeScript:

enum BooleanLikeHeterogeneousEnum {
  No = 0,
  Yes = 'YES',
}

Computed and Constant Members

Enum members can be constant (the default) or computed. A constant member can take certain forms.

enum FileAccess {
  // Constant members
  None,
  Read    = 1 << 1,
  Write   = 1 << 2,
  ReadWrite  = Read | Write,
  // Computed member
  GigaByte = 1 << 30
}

Here, ReadWrite is computed from the constant members Read and Write, and GigaByte is a computed value.

Union Enums and Enum Member Types

Sometimes you want to use enums as a union of each enum member. TypeScript allows enums to be used as types:

enum ShapeKind {
  Circle,
  Square,
}

interface Circle {
  kind: ShapeKind.Circle;
  radius: number;
}

interface Square {
  kind: ShapeKind.Square;
  sideLength: number;
}

Now, Circle.kind can only ever be ShapeKind.Circle.

Enums at Runtime

Enums are real objects that exist at runtime. For example, you can pass around enums and values from them like any other object:

enum LogLevel {
  ERROR, WARN, INFO, DEBUG
}

/**
 * This is equivalent to Object.keys(LogLevel).map(k => LogLevel[k]).
 * It returns the string names of each enum member.
 */
function printImportant(key: string, message: string) {
  const num = LogLevel[key];
  if (num <= LogLevel.WARN) {
    console.log('Log level key is:', key);
    console.log('Log level value is:', num);
    console.log('Log level message is:', message);
  }
}
printImportant('ERROR', 'This is a message');

Enums at Compile Time

TypeScript enums are removed during the compilation to JavaScript. However, reverse mappings are created by TypeScript as well:

enum Tristate {
  False,
  True,
  Unknown
}

let myState = Tristate[Tristate.True];
console.log(myState); // Outputs 'True'

Const Enums

Const enums are a TypeScript specific construct that can offer better performance compared to regular enums:

const enum Directions {
  Up,
  Down,
  Left,
  Right
}

let directions = [Directions.Up, Directions.Down, Directions.Left, Directions.Right];
// In generated code will become
// let directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];

Working with Flagged Enums

Sometimes you might want enums to support combinations of values, known as bit flags:

enum FileAccess {
  None,
  Read    = 1 << 0,
  Write   = 1 << 1,
  ReadWrite  = Read | Write,
  GigaByte = 1 << 2
}
let fileAccess = FileAccess.ReadWrite | FileAccess.GigaByte;
console.log(fileAccess); // Outputs 7

Best Practices for Using Enums

Consider using enums for better code clarity when handling a predefined set of values. Always name enums and their members in a meaningful way, and use const enums when optimizing for performance.

Conclusion

TypeScript enums add structure to your code by allowing you to define a set of well-named constants. They are an essential feature for developers requiring type safety, expressiveness, and readability in their codebases. Grasp these concepts and patterns, and enums will no doubt become a valuable tool in your TypeScript arsenal.