Stop Using Enums in TypeScript

Published on
Tags
#typescript
Authors

Enums have been a staple in TypeScript for quite some time, but they come with certain limitations and quirks. In fact, the TypeScript team now advises against using enums in many scenarios (as highlighted in the Typescript Handbook - Enums). Instead, modern TypeScript developers often prefer using objects with the as const assertion, which provides a more flexible and type-safe alternative.

Limited Type Safety

Numeric Enums can be assigned values outside their defined set, potentially leading to unexpected behavior. This behavior existed in TypeScript version 4.9.5 but was fixed in 5.0.4.

enum Direction {
  Up,
  Down,
  Left,
  Right,
}

// This is valid in TypeScript 4.9.5 😱
const dir: Direction = 5;

Generated Code Bloat

Non-const enums in TypeScript do not fit the concept of being ā€œa typed superset of JavaScriptā€. They break this principle by generating additional runtime objects, which introduce bidirectional mappings between enum keys and values. leading to larger JavaScript bundle sizes.

enum Roles {
  Admin,
  Writer,
  Reader,
}

This enum compiles to:

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

This additional code increases the size of your JavaScript output unnecessarily.

Increased Import Complexity

Using enums across multiple modules requires importing the enum in each module where it's used. This can lead to increased complexity and potential maintenance challenges, especially in large codebases.

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

const myRole: Roles = Roles.Admin; // Valid
const yourRole: Roles = "admin"; // Invalid

Better Alternative - Object Literals with as const

Instead of enums, use object literals with as const to achieve similar functionality with better type safety and minimal code bloat.

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

type Role = (typeof Roles)[keyof typeof Roles];

const myRole: Role = Roles.Admin;
const youRole: Role = "Admin";

This compiles to:

const Roles = {
  Admin: "Admin",
  Writer: "Writer",
  Reader: "Reader",
};
const myRole = Roles.Admin;
const youRole = "Admin";

Using object literals with as const provides the same benefits as enums without the drawbacks, making them a better choice for most TypeScript projects.

References

Typescript Handbook - Enums
Dont’s use ENUMS in Typescript, they are very dangerous - Ivanzaldivar
Why You Should Avoid Using Enums in TypeScript - Web Dev Tutor
Say Goodbye to Enums in TypeScript - Abdulwahab S.
Avoid using enums in Typescript - Niraj Chauhan
Don’t Use Typescript Enums - Vector Linux
Typescript Playground - Easy work with subset of enum Typescript has unions, so are enums redundant? - Stack Overflow