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.
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
#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)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
#enum Name {
Member = "value"
}
type Name = "value1" | "value2"More examples
#enum Difficulty {
Easy = 'easy',
Hard = 'hard'
}
console.log(Object.values(Difficulty))
console.log(typeof Difficulty)['easy', 'hard'] object
type Rank = 'bronze' | 'gold'
const current: Rank = 'gold'
console.log(current)
console.log(typeof Rank)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 constplustypeofpattern 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.