Daniel Rearden
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:
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")
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">
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]
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.
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!