Niraj Chauhan

Niraj Chauhan

#father#husband#SoftwareCraftsman#foodie#gamer#onepiece#naruto

🚦 Avoid using enums in Typescript

If you are coming to the typescript world from Java or from any other typesafe languages, then often your choice might be Enums for structuring something like loglevels, status, etc.

Stop!!!
Photo by Nick Wright on Unsplash

But in Typescript, things are little different.

Lets take a simple example.

enum Status {
    INIT,
    PROCESSING,
    DONE
}

Above, I have a simple enum with status values. Now when I try to save an entity in the DB with a status value:

function create(status: Status){
    db.payment.create({...values, status: status });
}

create(Status.PROCESSING);

The above code will create a record in the database and the status value will be 1. The reason for the status as 1 is due to how enums are transpiled at runtime.

If you try to log Status, the result will be unexpected:

{
  "0": "INIT",
  "1": "PROCESSING",
  "2": "DONE",
  "INIT": 0,
  "PROCESSING": 1,
  "DONE": 2
}

Above, the enums at runtime are transpiled to a key value pair where the key is the enum and the value is the index and also the reverse of it.

This index value can also create huge problems in your application. For eg: let's say I introduce a new status as CANCELLED in my enum:

enum Status {
    INIT,
    CANCELLED,
    PROCESSING,
    DONE
}

Now, the index value of the new status CANCELLED is 1 and PROCESSING is shifted to 2. This will break the system as all the PROCESSING records might be treated as CANCELLED.

One way to avoid this is to assign values to the enums:

enum Status {
    INIT = "INIT",
    CANCELLED = "CANCELLED",
    PROCESSING = "PROCESSING",
    DONE = "DONE"
}

But then instead of using enums, you can easily use simple objects. There is another problem with an enum, you cannot pass a string that actually is an enum value. Eg:

create("PROCESSING");

Above will give you an error: "Argument of type '"PROCESSING"' is not assignable to parameter of type 'Status'."

So what are the alternate options?

  • Unions
  • Objects

I would suggest to use Unions

type Status = "INIT" | "CANCELLED" | "PROCESSING" | "DONE";

Unions are the easiest way. But you can also try objects:

const STATUS = {
    INIT : "INIT",
    CANCELLED : "CANCELLED",
    PROCESSING : "PROCESSING",
    DONE : "DONE"
} as const

type ObjectValues<T> = T[keyof T]

type Status = ObjectValues<typeof STATUS>

Here as well, the type Status is actually an union.

There are some more pitfalls using const enum that is already published in the official docs of Typescript.

At the end, the only thing matters is the code readability and simplicity 😎.

typescriptenumsunionsobjects