Enums vs. String Literal Unions in TypeScript

Daniel Rearden

Fullstack Engineer
TypeScript
Enums vs. String Literal Unions in TypeScript

In TypeScript, there's two common ways to create a type with a distinct set of possible string values:

// Using an enum

enum PortfolioProjectStatus {
  DRAFT
  PUBLISHED
}

and

// Using a string literal union type

type PortfolioProjectStatus = "DRAFT" | "PUBLISHED"

So which approach should you go with? Let's look at a few key differences between the two:

Usage

Both enums and string literal unions provide tab completion in modern IDEs. However, because enums are both values and types, an enum must be imported any time it's used.

import { PortfolioProjectStatus } from '../types'

setStatus(PortfolioProjectStatus.DRAFT)

On the other hand, string literal unions only need to be imported when you need to reference the type (for example, when writing a function signature). Writing a string literal union value (with tab completion!) is as simple as typing ".

setStatus("DRAFT")

Extensibility

TypeScript does not provide a way to extend enums. On the other hand, for string literal unions you can simply do:

type PortfolioProjectStatusWithArchived = PortfolioProjectStatus | "ARCHIVED"

String literal unions also make it simple to exclude specific elements to create new types.

type PublishedPortfolioProjectStatus = Exclude<PortfolioProjectStatus, "DRAFT">

Iteration

Because enums are just objects under the hood, you can iterate over all possible values of an enum using a for...in loop. Since string literal unions are only types, this isn't possible. However, it's possible to define the set of possible of values first and then derive a union type from it, like this:

const possibleStatuses = ["DRAFT", "PUBLISHED"] as const
type PortfolioProjectStatus = typeof possibleStatuses[number]

Compilation

Since enums are both values and types, TypeScript converts them to objects at compile time. Because string literal unions are just types, they are removed at compile time, reducing your overall bundle size.

Conclusion



That said, there are plenty of valid use cases for enums. For example, if you need to map a set of values to a different set of keys, this can't be done using a simple union type and enums are a great fit.

enum Colors {
  Red = '#FF0000',
  Green = '#00FF00',
  Blue = '#0000FF',
}

Similarly, if you want to be able to compare values relative to their position in the enum or some other numeric values, enums can help.

enum Colors {
  Red = 1,
  Green = 2,
  Blue = 3,
}

Colors.Blue > Colors.Red // true
Colors.Green > Colors.Blue // false

However, if you're looking for a type to capture the possible values for a particular variable, string literal unions are a superior choice.

TypeScript is a powerful, flexible language that lets developers choose the best tool for the job. Make sure you choose carefully!

Partner With Daniel
View Services

More Projects by Daniel