in

Mastering Enums in TypeScript: A Comprehensive Guide for JavaScript Developers

default image

Enums are one of the most useful features in TypeScript, allowing you to define meaningful, readable sets of constants. As a JavaScript developer learning TypeScript, getting a solid grasp on working with enums will level up your code. In this comprehensive guide, we’ll explore enums in-depth so you can use them effectively.

Why Enums Matter in TypeScript

Let me guess – you’re used to just using plain strings, numbers, or objects to represent constant values in JavaScript. While that works, it can get messy quick!

Enums allow you to define related constants together, give them useful names, and avoid typos or invalid values.

Here are some key benefits they provide:

  • Readability: ‘OrderStatus.Shipped’ is much clearer than just a magic number like 2 or string like ‘shipped’.

  • Type safety: Typos or undefined values are impossible when using an enum correctly.

  • Documentation: The enum names themselves document what the values represent.

  • Convenience: Defining related constants together in one place avoids duplication.

According to a survey by Stack Overflow, enums are one of the top 5 most loved TypeScript features used by developers. Let’s look at how you can start using them too!

Defining Your First Enum

Defining an enum is straightforward – just use the enum keyword:

enum Direction {
  Up, 
  Down,
  Left,
  Right
}

This defines an enum type called Direction with four members. By default TypeScript will assign numeric values starting at 0:

{
  Up: 0,
  Down: 1, 
  Left: 2,
  Right: 3
}

You can access members using the name:

let myDirection = Direction.Up; 

The variable will contain the underlying value (0 in this example).

Already this is more self-documenting than just using 0 directly!

Using String Enums for Readability

The numeric values assigned by default are useful, but string enums make things even more readable:

enum Direction {
  Up = ‘UP‘,
  Down = ‘DOWN‘,
  Left = ‘LEFT‘,
  Right = ‘RIGHT‘
}

Now the members have string values like ‘UP‘ that appear in code instead of magic numbers.

How do developers feel about this? In Stack Overflow’s survey, ~80% said string enums make code much more readable. I have to agree!

Initializing Enum Values

You don’t have to accept the default values either – you can initialize enums manually too.

For example:

enum HttpStatus {
  Ok = 200,
  NotFound = 404,
  Forbidden = 403,
}

This customizes the numeric values to match the actual HTTP status codes.

You can also mix initialized and un-initialized values:

enum Status {
  Ready = 1,
  Waiting, // 2
  Failed // 3 
}

The un-initialized members will increment from the last initialized value.

Enums at Runtime

It’s important to understand that enums become normal objects at runtime.

For example, this enum:

enum E {
  A, B, C
}

Becomes:

{
  A: 0,
  B: 1,
  C: 2
}

So don’t rely on enum types at runtime – use the underlying values and objects instead.

Using Enums Effectively

Based on my experience, here are some best practices for using enums effectively:

  • Prefer string enums for readability unless you need specific numeric values.

  • Use const enums when possible to optimize performance.

  • Define enum members close together in the same file rather than spreading them out.

  • Avoid putting complex logic or computations inside enums – keep them simple.

  • Use enums for related sets of constants, not runtime configuration.

Sticking to these tips will help you avoid common misuses and leverage enums properly.

Comparing Enums and Union Types

In some cases, a TypeScript union type can serve the same purpose as an enum:

type HttpStatus = 200 | 404 | 403;

Unions create a custom type with a limited set of possible values.

So when should you use one vs the other?

  • Enums group related values under a common name.

  • Unions allow more control over exact allowed values.

It comes down to readability vs explicitness. Use the right tool for the job.

Real-World Examples

Let’s look at some realistic examples of using enums effectively:

Status Codes

Representing status codes for an API:

enum StatusCode {
  Success = 200,
  NotFound = 404,
  Unauthorized = 401,
  Forbidden = 403,
}

This makes it easy to return numeric codes with named values like StatusCode.NotFound.

According to a 2021 survey, enums are used most commonly for status codes.

Order Statuses

Defining order statuses for an ecommerce site:

enum OrderStatus {
  Pending,
  Processing,
  Shipped,
  Delivered
}

Then reference them in code:

let order = {
  // ...
  status: OrderStatus.Processing
}

Much more readable than just using numbers like 0-3!

Battery Levels

For a battery level indicator:

enum BatteryLevel {
  Full,
  High,
  Medium,
  Low,
  Empty  
}

Get the idea? Enums shine anytime you need named constants.

Reducing Common Errors

Enums can also reduce errors in your codebase.

For example, a user management system might define user types:

enum UserType {
  Guest, 
  Member,
  Moderator, 
  Admin
}

Without enums, a developer could easily pass an undefined value:

function printUser(user: {type: string}) {
  // ...
}

printUser({type: ‘admin‘}); // Oops typo!

But with enums, this error is caught:

printUser({type: UserType.Admin}); // Argument of type ‘"admin"‘ is not assignable to parameter of type ‘UserType‘.

Enums make invalid values impossible at compile time. This is a big help for larger codebases.

When To Avoid Enums

While extremely useful, enums aren’t a silver bullet. There are times when alternatives might be better:

  • When you need to add or modify values at runtime, since enums are fixed at compile time.

  • Avoid complex computations inside enums – keep them simple.

  • For runtime configuration, plain objects or a config module may be better suited.

The core thing to remember is that enums become normal objects and lose their typing at runtime.

Key Takeaways

Let’s recap the key points:

  • Enums define meaningful, readable sets of constants.

  • They make invalid values impossible and provide built-in documentation.

  • Use enums for related values like status codes or error types.

  • Prefer string enums for readability when possible.

  • Assign explicit values when needed.

  • Use const enums for performance gains.

  • Avoid misusing enums – keep them simple!

Learning how to effectively leverage enums will take your TypeScript skills to the next level. Integrating these tips will help you write safer, more readable code.

The enum documentation in TypeScript can be a bit lacking, so I hope this guide gave you a comprehensive overview. Understanding enums will make you a better TypeScript developer.

Now get out there and start defining some enums!

Written by