All Articles

What are Enums in TypeScript, and When Should you use Them

Joe PreviteJoe Previte

Enums are a concept that exists in TypeScript (not JavaScript) and can help you in certain situations.

In this article, we’ll look at:

  • What they are
  • What happens when you compile to JavaScript
  • When should you use them

We’ll create our own example and then find an example from an open source project. By the end, you’ll have enough knowledge to explain them to your coworker and know when to use them.

Show me the code

If you want to jump straight into code examples, take a look at these TypeScript playground links:

Why should I care about Enums?

Before we dive into this concept, I want to talk about why you should even care. Why read this article?

They’re part of what I would consider TypeScript fundamentals. Chances are you’ll either use them yourself or run into them while working in someone else’s codebase. Therefore, you should know what problem they solve and why someone would use them.

They let you define a set of constraints.

Think of padding in CSS. You can’t do paddingDiagonal: 12px right? That’s because padding is constrained to 4 directions: top, right, bottom, and left. If we were to build our own CSS parser in TypeScript, we could turn these rules into logical constraints using Enums.

enum PaddingDirection {
  Top,
  Right,
  Bottom,
  Left,
}

As you can see, this gives us a way to define these constraints and use PaddingDirection in later parts of our CSS parser.

What are Enums?

Let’s continue our dive into Enums with another example that you’re probably familiar with.

Take for example, the logic for determining whether or not you have a streak in Duolingo:

50-day streak in DuolingoLoading

The outcome of that function could either be:

  • Yes
    • There is a streak
  • No
    • There is a gap between the days, rest streak
    • The days are the same; keep the streak as is

We could represent these states more specifically with:

  • UPDATE - streak should be updated
  • RESET - streak should be reset
  • NONE - streak should stay the same

Since we have these states constrained to three specific actions, we can represent these using an Enum:

enum StreakAction {
  Update = "UPDATE",
  Reset = "RESET",
  None = "NONE"
}

One difference between this example and the PaddingDirection one is that we’re using a String enum. We use this over a Numeric enum because they are easier to debug at runtime. Why? Well, they’re constrained to a string literal instead of a number which would look something like this:

enum StreakUpdate {
  Update = 1,
  Reset,
  None,
}

Both get the job done, but the string enum is better in my opinion because they align with my mental model of Enums. I associate them with strings since that’s what I expect. I also think this makes it easier to maintain and debug for other developers.

What happens to Enums at compile time?

Speaking of debugging, let’s discuss what happens to Enums at compile time. First, we know that this concept or structure isn’t native to JavaScript. That means they have to be transpiled to something JavaScript-y.

Fortunately, the TypeScript Playground shows you exactly what your code compiles to. Let’s add the StreakAction string enum and analyze the output.

TypeScript Playground 1 - StreackActionLoading

Without getting into the weeds of everything happening in the transpiled code, we can see that in JavaScript, the structure is represented similar to this:

const StreakAction = {
  "Update": "UPDATE",
  "Reset": "RESET",
  "None": "NONE"
}

As you can see, there is nothing scary or surprising here. If we were to use the numeric enum like before, the output is a little bit more difficult to read:

TypeScript Playground 2 - StreackActionLoading

You might not think so.

Experiment with both and see which one you prefer.

When should you use them?

The real answer is? Maybe never. A large group of folks in the TypeScript community avoids them because you can use objects with as const to achieve a similar structure.

The biggest argument in favor of this format over TypeScript’s enum is that it keeps your codebase aligned with the state of JavaScript, and when/if enums are added to JavaScript, then you can move to the additional syntax.

The TypeScript Handbook even notes that using objects instead of enums keeps your codebase more in line with today’s JavaScript. This is my personal preference as well.

That said, they’re still used by many people. Here’s an example of a GitHub project in the Vue community called vue-vben-admin with 12.9k stars. They have a folder dedicated to enums:

Keep these points in mind when deciding whether or not you should use them.

String Literals as an alternative to Enums

A real-world example that uses string literals as opposed to enums is the decoding attribute on the <img /> HTML element.

In case you haven’t used it, the decoding attribute, “provides an image decoding hint to the browser.” There are only three valid values: sync, async or auto. This is defined using a union type of string literals in the dom types here:

/*Provides special properties and methods for manipulating <img> elements*/
interface HTMLImageElement extends HTMLElement {
    //...
    decoding: "async" | "sync" | "auto";
    //...
}

As you can see, this achieves the same effect of enums but is less code and arguably easier to maintain.

Resources

For more in-depth info on Enums, take a look at these sections in the TypeScript handbook:

Summary

In summary, we discussed Enums and how they’re used. We looked at Enums in compiled JavaScript. Lastly, we talked about when you should use them, including some open source examples.

You’ve leveled up your TypeScript fundamentals and now understand an important concept in the language. This is valuable because you can now work with Enums in any codebase and also explain to others what they are and when to use them.

Now, play around with them and solidify this new knowledge with hands-on practice. 😄

Thank you to TypeScript Community

I want to take a second to say thank you to @alexdotjs, @t3dotgg, @hasparus, and others for feedback on Twitter suggesting I add string literals as an alternative to enums.