ComparisonComparisons

enum vs union

enum creates a named set of constants that also exists at runtime as an object, while a literal union is just a type, lighter and with no JavaScript code at all.

On this page

Side-by-side comparison

#
  • Exists in the running program (JavaScript)

    enum
    Yes, as an object
    Unia (A | B)
    No, only at compile time
  • Writing a value

    enum
    Through a member name, e.g. Difficulty.Hard
    Unia (A | B)
    Directly as a literal, e.g. "hard"
  • Adds code to the bundle

    enum
    Yes
    Unia (A | B)
    No
  • Iterating over the variants at runtime

    enum
    Yes, e.g. with Object.values()
    Unia (A | B)
    No, a separate array is needed
  • Combining with other types (union, intersection)

    enum
    Limited
    Unia (A | B)
    Natural, it’s a union too
  • Typical use

    enum
    When you also need an object at runtime
    Unia (A | B)
    Data from an API, props in JSX, most new code

Example

#
TypeScript
enum StatusEnum {
  Draft = 'draft',
  Published = 'published'
}

type StatusUnion = 'draft' | 'published'

const fromEnum: StatusEnum = StatusEnum.Published
const fromUnion: StatusUnion = 'published'

console.log(fromEnum, fromUnion, fromEnum === fromUnion)
Result
published published true

Definition and usage

#

Both constructs restrict a variable to one of a few allowed variants, and both work well for a status, a difficulty level, or a user role. In both cases the compiler reports an error if you assign a value outside the set.

They differ in what comes out of compilation. enum generates a real JavaScript object, which you reference by name (Difficulty.Hard), and you can inspect its values while the program runs, e.g. with Object.values(). A literal union ("easy" | "hard") exists only at compile time and vanishes without a trace. Instead of a member name, you type the value directly, meaning the literal itself.

When you also need an object in the running program, for example to iterate over the variants or to map a number to a name, enum can be convenient. In most new code, especially with data from an API and in JSX, a literal union is simpler and lighter, because it adds no code to the bundle.

Syntax

#
Syntax
enum Name {
  Member = "value"
}

type Name = "value1" | "value2"

More examples

#
enum exists as an object at runtime
TypeScript
enum Difficulty {
  Easy = 'easy',
  Hard = 'hard'
}

console.log(Object.values(Difficulty))
console.log(typeof Difficulty)
Result
['easy', 'hard']
object
A literal union leaves no trace in JavaScript
TypeScript
type Rank = 'bronze' | 'gold'

const current: Rank = 'gold'
console.log(current)
console.log(typeof Rank)
Result
gold
undefined

Rank is only a type here, so after compilation it doesn’t exist as a variable. typeof on a name that doesn’t exist doesn’t throw, it just returns "undefined".

Good practices

#
  • If the data comes from an API as strings, a string enum and a literal union are nearly equivalent. Choose the union unless you need an object to iterate over.
  • A numeric enum (enum Status { Draft, Published }) can be confusing in logs, because it prints a number, not a name. A string enum or a union is easier to read.
  • The as const plus typeof pattern gives an effect similar to an enum: a list of values to loop over and a union type, both from one constant.

Related entries

#

Spotted a mistake or missing an example? Write to us.