Enums For The Uninitiated

July 12, 2018

Before TypeScript I have never used or heard of enums. So needless to say when I saw them in the docs I was confused.

I was so used to describing the shape of data structures with interfaces that I didn’t understand what the benefits are of using enums.

I was also confused because enums hold values as well as being a type. I didn’t think of TypeScript as something that gives me things to hold values with.

So in order to understand I had to look into it some more and here is how I understand enums:

Enums give you an easy way to logically group constants in order to make your code easier to read and understand.

So if you have a bunch of constant values in your program that logically belong together. Like the days of the week, directions (up, down, left, right) or return types of an asynchronous operation for example you can use enums to group them and use them in your code.

Enums give you type and data structure in one go without a lot of work.

If you declare the following enum:

enum Directions {
  Up,
  Down,
  Left,
  Right
}

You are doing a bunch of things in one go:

  • Declaring a type Directions
  • Assigning members to the enum
  • Implicitly assigning values to the members of the enum

The assigned values are numbers by default. Starting at 0 and incremented with each following property.

So it’s kind of like a JavaScript object just that values are assigned automatically.

You can change the starting point of the assigned numbers by assigning a custom number to the first property.

enum Directions {
  Up = 1,
  Down,
  // ...
}

This means the increment starts at 1 instead of 0. That can be useful if you want to make sure that the first value can never be falsy.

You can also manually assign values to all properties of the enum. They can be of type number or string.

So now you can go ahead and use these constants in your code like:

if (direction === Directions.Up) {
  // Do things.
}

Simple and convenient.

The TypeScript docs say this:

Enums allow us to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases.

After having used enums a little bit in recent projects I find them very convenient and useful. I think especially “documenting intent” is what they do quite well in a convenient way.

One cool thing about enums with numeric values, is that you can retrieve the member name of a numeric value by accessing it with the number, in our case accessing a Directions member with the number 1 returns the string "Up".

Directions[1] // returns "Up"

It’s cool but so far I have never needed it. To make this possible during runtime enums get compiled to this:

var Directions;
(function (Directions) {
    Directions[Directions["Up"] = 0] = "Up";
})(Directions || (Directions = {}));
var direction = Directions.Up;
var nameOfA = Directions[direction]; // "Up"

It’s an iife that gets passed an object. That function then assigns numbers to the member names and member names to the numbers.

This line does two things at the same time.

Directions[Directions["Up"] = 0] = "Up";

Directions["Up"] = 0 assigns the number to the member name and because that line is a JavaScript expression it evaluates to a value and that value is the number that was assigned.

This means that the whole line evaluates to Directions[0] = "Up";.

So what you end up with in JavaScript is:

{
  0: "Up",
  Up: 0
}

Pretty neat right? But let’s say resources are scarce and you don’t need to access member names then you can use const enums.

They evaluate to their respective values. The enum code is completely removed during compilation. This means:

const enum Directions {
  Up = 1,
  Down
}

if (direction === Directions.Up) {
  // Do things.
}

Is compiled to:

if (direction === 1) {
  // Do things.
}

So that’s my take on enums. After getting comfortable using enums for a bit, I find places where they can be useful all the time and I really like how they do make intent very explicit. Enums can make your code easier to read.