DEV Community

Cover image for Don't use Enums in Typescript, they are very dangerous ๐Ÿ˜จ
Ivan Zaldivar
Ivan Zaldivar

Posted on

 

Don't use Enums in Typescript, they are very dangerous ๐Ÿ˜จ

Have you ever wondered why TypeScript experts recommend avoiding the use of ENUMs? While they may seem like a useful tool for defining a set of constant values, they are actually very dangerous. In this article, we will show you why and how to avoid their use. You'll discover that there are much safer and reliable alternatives that you can use instead. Get ready to be surprised! ๐Ÿ˜ฑ

Enum's emitย code

One of the reasons why it is recommended not to use it is due to the generation of code at the time of compiling the app. ENUMs generate additional code at compile time, which increases the size of the final file. This can have a negative impact on the loading speed and performance of the app.

Define our Roles enum

enum Roles {
  Admin,
  Writer,
  Reader
}
Enter fullscreen mode Exit fullscreen mode

Output (Build-generated code)

var Roles;
(function (Roles) {
    Roles[Roles["Admin"] = 0] = "Admin";
    Roles[Roles["Writer"] = 1] = "Writer";
    Roles[Roles["Reader"] = 2] = "Reader";
})(Roles|| (Roles = {}));
Enter fullscreen mode Exit fullscreen mode

While it's true that this detail is fixed if you use a constant enum, but I've been on multiple projects and seen people using regular enums everywhere and wonder why their output is so big.

Postada: I have been one of those people. ๐Ÿ˜

This may seem trivial, but imagine that you have files shared between the "Frontend" and "Backend" you can end up with quite heavy bundles.

Okay, that's one thing, and we can handle that by enforcing the constants. But there is also this unpleasant ambiguity.

Numeric typesย usafe

Yes, you read it right. This is not clickbait. Regular numeric enums, such as in an enum where you don't set string values, are not type safe! If we look back at the Roles enumeration from earlier, a function that takes user roles also takes any numeric value instead.

enum Roles {
  Admin,
  Writer,
  Reader
}

declare function hasAccess(role: Roles): void;

hasAccess(10);
// โ˜๏ธ Worst of all, this is ok! ๐Ÿ˜ฑ
Enter fullscreen mode Exit fullscreen mode

As you may have noticed, when the hasAccess(10) function is called, a numeric value is being passed that is not part of the enum Roles, this is allowed in TypeScript, and this is what is considered a problem, since it allows the entry of unexpected and unverified values which can cause security and performance problems in the app.

String ENUM's are namedย types

In a world where structural types are common, ENUMs choose to be a named type. This means that even if the values are valid and supported, they cannot be passed to a function or object where a string enumeration is expected. Let's see this example:

enum Roles {
  Admin = 'admin',
  Writer = 'writer',
  Reader = 'reader'
}

declare function hasAccess(role: Roles): void;

hasAccess('admin') // Invalid.
hasAccess(Roles.Admin) // Valid.
Enter fullscreen mode Exit fullscreen mode

As you can see enums are a named type and only accept enum-specific values, not compatible or similar values, which can lead to compatibility issues and should be carefully considered when designing and using enums.

Solution

A much safer alternative and guarantee of compatibility is the use of objects. Let's see the following example:

const Roles = {
  Admin: "admin",
  Writer: "writer",
  Reader: "reader"
} as const;

// Convert object key in a type
type RoleKeys = typeof Roles[keyof typeof Roles]

declare function hasAccess(role: RoleKeys): void;

// ๐Ÿ’ฅ Error!
move('guest');

// ๐Ÿ‘ Great!
move('admin');

// ๐Ÿ‘ Also great!
move(Roles.Admin);
Enter fullscreen mode Exit fullscreen mode

Conclusion

ENUMs may seem like a useful tool for defining a set of constant values, but they are actually very dangerous. Excessive use of regular ENUMs can lead to code size issues, security issues, scalability issues, and maintainability issues.

Instead of using ENUMs, it's better to opt for objects or types. Objects are flexible and scalable, which means that they can be modified at runtime and new values can be added. Types are also flexible and scalable, and also offer better code clarity and readability. Also, objects and types are less prone to bugs and security issues compared to ENUMs. In short, using objects or types instead of ENUMs is a better option in terms of flexibility, scalability, clarity, readability, and security.

Follow meย โค๏ธ

Top comments (6)

Collapse
 
bcostaaa01 profile image
Bruno

Good article!๐Ÿ‘

You could also use enums in combination with interfaces, types of classes to encapsulate the behaviour of the enum, but, to be honest, I donโ€™t really see a usage of an enum. You have far better solutions which are type safer such as string literals:

type Roles = 'admin' | 'writer' | 'reader';
Enter fullscreen mode Exit fullscreen mode

This adds the benefit of preventing typos because that allow you to match the exact string value that is desired to be used. You have other better solutions as well, but this one should be a good one to replace the usage of an enum.

Collapse
 
trueromanus profile image
Roman Vladimirov

ENUMs generate additional code at compile time, which increases the size of the final file.

This is a rather strange statement considering that typescript always generates more code than you write.

Collapse
 
amanecer2 profile image
amanecer • Edited

Great article!
If you write export const enum you won't generate single value.
And it will compile to nothing and in thee code it will have the hard code value

export const enum Enum {
FIRST = 'name'
}

Collapse
 
trueromanus profile image
Roman Vladimirov

As you can see enums are a named type and only accept enum-specific values, not compatible or similar values, which can lead to compatibility issues and should be carefully considered when designing and using enums.

Well, i.e. in the paragraph above, you write how bad it is that the typescript does not check that the value is in the range of the Enum. And here you write how to make it possible for strings, which will eventually lead to the same problems in the future as in the previous example.

In short, using objects or types instead of ENUMs is a better option in terms of flexibility, scalability, clarity, readability, and security.

May be you share examples in post?

Collapse
 
lexlohr profile image
Alex Lohr

I prefer unions. More portable, you still get auto-complete and they are compiled away from the code into types only, without ever reaching the JavaScript output.

Collapse
 
brense profile image
Rense Bakker

Articles like these are great ๐Ÿ‘ very good explanation of the problem and the solution! Many thumbsup ๐Ÿ‘๐Ÿ‘๐Ÿ‘

Visualizing Promises and Async/Await ๐Ÿค“

async await

โ˜๏ธ Check out this all-time classic DEV post on visualizing Promises and Async/Await ๐Ÿค“