Typescript Enums are bad!!1!!!1!!one - Are they really?
Tags: javascript, beginners, bestpractices, typescript
Published: 2022-03-31 13:46:06

---
title: Typescript Enums are bad!!1!!!1!!one - Are they really?
published: true
description: are Enums useful or evil? as usual - it depends.
tags: #javascript, #beginners, #bestpractices, #typescript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bgtc2snk58rydivaz0jl.png
---

Recently in our projects we started using Typescript and among many features we extensively adopted, there are **Enums**.  

I find them very handy and readable compared to having to export a bunch of individual constants or creating an Object of Constants, or building Maps.

Every now and then though, some developer seem to struggle with Enums, either they have problems with logging their value, or comparing it to some runtime value or simply gets influenced by some post on the internet.  

And there are many:

- [The Dangers of TypeScript Enums](https://dev.to/azure/the-dangers-of-typescript-enums-55pd)

- [Alternatives to Typescript Enums](https://maxheiber.medium.com/alternatives-to-typescript-enums-50e4c16600b1)

- [Are Enums Redundant?](
https://stackoverflow.com/questions/40275832/typescript-has-unions-so-are-enums-redundant)

- [Put the TypeScript enums and Booleans away]
(https://blog.logrocket.com/put-the-typescript-enums-and-booleans-away/)

- [Tidy TypeScript: Prefer union types over enums](https://fettblog.eu/tidy-typescript-avoid-enums/)

and not only with Typescript:

- [Use Enums with caution](https://www.planetgeek.ch/2009/07/01/enums-are-evil/)

- [Stop wasting time with enums](
https://volaresoftware.com/en/technical-posts/stop-wasting-time-with-enums-in-c)

- [Enums are Evil: The State Pattern](https://colourcoding.net/2009/07/25/enums-are-evil-the-state-pattern/)


Honestly,  I don't quite get it.

# The problems with Enums

## the _Compiled Enums are weird_ argument

True, Typescript Enums when compiled to plain javascript are **ugly**.

These nice and tidy Enums

```
enum UserStatus {
    REGISTERED,
    INACTIVE,
    NOT_FOUND,
    BANNED
}
```
are compiled to:

```
var UserStatus;
(function (UserStatus) {
    UserStatus[UserStatus["REGISTERED"] = 0] = "REGISTERED";
    UserStatus[UserStatus["INACTIVE"] = 1] = "INACTIVE";
    UserStatus[UserStatus["NOT_FOUND"] = 2] = "NOT_FOUND";
    UserStatus[UserStatus["BANNED"] = 3] = "BANNED";
})(UserStatus || (UserStatus = {}));

```

But this is true only because we are using **_Numeric_ Enums** ( which are the default) instead of **_String_ Enums** (which for me make more sense).

```
enum UserStatus {
    REGISTERED="registered",
    INACTIVE="inactive",
    NOT_FOUND="notFound",
    BANNED="banned"
}
```

compiles to:
```
var UserStatus;
(function (UserStatus) {
    UserStatus["REGISTERED"] = "registered";
    UserStatus["INACTIVE"] = "inactive";
    UserStatus["NOT_FOUND"] = "notFound";
    UserStatus["BANNED"] = "banned";
})(UserStatus || (UserStatus = {}));

```

Which is ... still _quite bad_. 
But is it really?

Despite the ugly _var_ it is just an [IIFE](https://developer.mozilla.org/de/docs/Glossary/IIFE), an _Immediately Invoked Function Expression_ which assigns strings values to the properties of an object.  

Sure, probably in plain JS I would have directly written:

```
const UserStatus = {
NOT_FOUND= "notFound"
// and so on..
}
```

or even better ( if you really want to prevent your _constants_ to be changed _at runtime_)
```
const CustomErrors = Object.freeze({
PLAYER_NOT_FOUND= "playerNotFound"
})
```

but it is not _soooo_ weird as you might think at a first look and anyway,  **what I am looking at while reviewing and debugging is Typescript not Javascript**.  Or do we want to start wining and arguing that **even bundled and minified code loaded in the browser is not readable**?

## The _Union Types are better_ argument

Typescript have another interesting feature which are **Union Types**.

These can be use to "lock" the type/value of a string to only a certain values.  Similarly to Enums.

```
type UserStatus = "registered" | "inactive" | "notFound" | "banned" 
```

This is compiled to:

```
  //
```

Yes,  it's not a mistake.  To **nothing**.
Because **types are not compiled** to javascript.

They don't exist in javascript code at all.

So, would you say it is more clear and readable to look at the compiled code?

Is it more readable in Typescript?
This is a matter of tastes, honestly.
I am used to see values that are constants as ALL_CAPITALIZED and the usage of Enums seems more straightforward.

```
const status = UserStates.REGISTERED
console.log(status)
```

(True, some IDE are now smart enough to suggest the values available in the type, but you are still relying on "strings", not on what look like constants, and if renamed/replaced have effect everywhere)

Personally,  I use Union Types when my String has 2 or max 3 values,  as soon as the options become more, I switch to Enums.

## The _Enums increase the size of your code_ argument

Yes,  Enums are compiled to something, while UnionTypes are simply stripped away, so your Javascript will be bigger.
While it be significantly bigger? Is it relevant for your project?
This depends on where your project will run, and on how many Enums you have.

Personally,  this is for me not even an argument...


## the Enums are _hard to map and compare_ argument

I heard this a few times, but honestly I never really got the point.

You can easily compare an Enum with a string (imagine you are receiving a value at runtime from a querystring or a database

```
console.log("registered" === UserStatus.REGISTERED)
```

But, you will say,  if I want to compare a string at runtime with my Enum, Typescript will complain that the signature of my method is wrong!

Is it?
It is NOT,  the following is perfectly valid Typescript

```
const isBanned =(status:string)=> status  === UserStatus.REGISTERED 
```
nor it is when you are relying on Typed Objects.

```
type User = { 
   status:UserStatus
}
const isBanned =(user : User)=> user.status  === UserStatus.REGISTERED 
```

If, for some reasons you end up having troubles with the Type your function signature is expecting, then I suggest using **Union Types** there!

```
const isBanned =(status : string | UserStatus)=>status  === UserStatus.REGISTERED 
```
or if anywhere else in the code you typed the value you will be received at runtime as `string` and you want to pass it to a function which expects an `enum`,  then just cast it.

```
let runtimeStatus:string;
type isBanned  = (status : UserStatus)=> boolean

// then later on:
runtimeStatus:string  = "registered"
isBanned(runtimeStatus as UserStatus)
```

## The _they are useless at runtime_ argument
This is a false argument for typescript in general, let alone Enums.

The fact is, Enums are great for the coding experience, any comparison at runtime works because they are just strings in the end ( remember, **types are not compiled to js**)

This TS:
``` 
const isBanned =(status : UserStatus)=> status  === UserStatus.REGISTERED 
```
becomes this JS:
```
const isBanned = (status) => status === UserStatus.REGISTERED;
```

Agree, if at runtime we receive a value which is not within the Enums, we will not get any error, but that is no surprise,  the same happens for any type.  If we want to validate that the value is within the values listed in the Enum, we can simply _iterate_ over the keys or values. ( see below) 

and agree, if at runtime some code tries to change the values of one of your enums, that would not throw an error and your app could start behaving unexpectedly ( that is why `Object.freeze` could be a nifty trick) but... what's the use case for that?

- an absent-minded developer might assign somewhere a different value ( using the assign operator instead of the comparison) 
```
if(UserStatus.PLAYER_NOT_FOUND = "unexpected_error")
/// ops..
if(CustomErrors.PLAYER_NOT_FOUND == "unexpected_error")
```
Then Typescript would immediately notify the problem.

- a malicious developer might force the casting to silence that error?

```
(CustomErrors as any).PLAYER_NOT_FOUND = "aha!!Gotcha!"
```

In this case Typescript can't do much,  but... wouldn't such code be noticed during your Code Review? (because you are doing PullRequests, right? right?!?)


## The _Enums are difficult to Iterate over_ argument
Again,  not an argument for me.

Do you want the string values?

```
console.log(Object.values(UserStatus))
```

Do you want the "Constants" keys?
```
console.log(Object.keys(UserStatus))

```

### The _better use a Class with Static values_ argument

Somewhere I also read the suggestion to use static readonly within a Class which will basically act as an holder of Enums.

```
class UserStatus {
    static readonly REGISTERED="registered"
    static readonly INACTIVE="inactive"
    static readonly NOT_FOUND="notFound"
    static readonly BANNED="banned"
}
```
This works, honestly I don't see much of an improvement, nor I know if it _"solves"_ the arguments that people try to address.
What is interesting to note is that this approach compiles to this in Javascript

```
class UserStatus {
}
UserStatus.REGISTERED = "registered";
UserStatus.INACTIVE = "inactive";
UserStatus.NOT_FOUND = "notFound";
UserStatus.BANNED = "banned";

```
which in the end is not much different from having a bunch of static consts exported individually in a module.


## Recap
I am perfectly aware that here I am discussing only the String enums, while there are [many other types](https://www.typescriptlang.org/docs/handbook/enums.html#heterogeneous-enums) and there are some [pitfalls](https://www.typescriptlang.org/docs/handbook/enums.html#const-enum-pitfalls)

The fact is,  so far **I never really felt the need for other types**, and everyone complaining about enums was always using Number Enums when String Enums would have been a better choice.
For me StringEnums work perfectly, allow clean, readable, organised list of values and you can benefit from autocomplete features from your IDE, you have warnings at compile time if you use it wrong ( trying to pass around values that are not enums).
But maybe I am missing something.. For example, I really can't figure out a scenario where I would need to write code myself to implement a [ReverseMapping](https://www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings) ( which is not done by Typescript automatically as for Numeric Enums) like described [here](https://stackoverflow.com/questions/17380845/how-do-i-convert-a-string-to-enum-in-typescript)
 
Maybe I have been always using enums wrong ( probably because I always mostly worked with languages which had no real Enums) and my default approach is having string constants rather than numeric enums, but in my experience I hardly encountered such need, so I never understood all this fuzz and worry about Typescript Enums.  

What's your take on that?


----
Photo by <a href="https://unsplash.com/@glenncarstenspeters?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Glenn Carstens-Peters</a> on <a href="https://unsplash.com/s/photos/lists?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>