DEV Community

Cover image for TypeScript Enums: 5 Real-World Use Cases
Alex
Alex

Posted on • Updated on • Originally published at Medium

TypeScript Enums: 5 Real-World Use Cases

If you’re looking to make your TypeScript code more organized, enums are a powerful tool. They group related values together, giving your code better structure and readability. Let’s dive in and explore how to use them!

In TypeScript, enums are declared using the enum keyword. For example, you could create an enum to represent the different fruit prices as follows:

Example 1: Basic Enum Structure — Key Movements

Demonstrates a common use case for enums: making choices within a limited set clearer.

enum Movement {
  Up = "UP",
  Down = "DOWN",
  Left = "LEFT",
  Right = "RIGHT"
}

function handlePlayerInput(key: string) {
  switch (key) {
    case Movement.Up:
     // Move player character up
      break;
    case Movement.Down:
      // Move player character down
      break;
    // ... other cases
  }
}
Enter fullscreen mode Exit fullscreen mode

This demonstrates the fundamental syntax for defining an enum. The Movement enum has four members representing directions.

Example 2: Enums with Values

Enums can have associated values (numbers, strings, etc.). Here, StatusCode associates HTTP status codes, ensuring type-safety and preventing the use of arbitrary numbers.

enum StatusCode {
  OK = 200,
  BadRequest = 400,
  NotFound = 404
}

function handleResponse(code: StatusCode) {
  if (code === StatusCode.OK) {
    // Handle successful response
  } else if (code === StatusCode.NotFound) {
    // Handle resource not found
  } 
  // ... other cases
}
Enter fullscreen mode Exit fullscreen mode

HTTP status codes

Image source: https://restfulapi.net/http-status-codes

Example 3: Enum from Redux Toolkit

Redux Toolkit is a popular library for state management in React applications. It makes heavy use of TypeScript for type safety.

Redux logo

This enum defines distinct states for asynchronous actions (like fetching data). This is common in state management libraries.

enum PayloadActionLoadingState {
  Idle = "idle",
  Loading = "loading",
  Failed = "failed",
  Success = "success" 
}
Enter fullscreen mode Exit fullscreen mode

Example 4: Enum as a Discriminated Union

This enum defines two possible shapes: Circle and Rectangle. It acts as a foundation for ensuring type safety when working with different shapes.

Each shape type (Circle, Rectangle) is represented as a member of the ShapeType enum.

The Shape interface has a type property that must be a member of the ShapeType enum.

enum ShapeType {
    Circle = "Circle",
    Rectangle = "Rectangle"
  }

  interface Shape {
    type: ShapeType; 
  }

  interface Circle extends Shape {
    type: ShapeType.Circle;
    radius: number;
  }

  interface Rectangle extends Shape {
    type: ShapeType.Rectangle;
    width: number;
    height: number;
  }

  function calculateArea(shape: Shape): number {
    switch (shape.type) {
      case ShapeType.Circle:
        const circle = shape as Circle; // Type assertion to Circle
        return Math.PI * circle.radius * circle.radius;
      case ShapeType.Rectangle:
        const rectangle = shape as Rectangle; // Type assertion to Rectangle
        return rectangle.width * rectangle.height;
      default:
        throw new Error("Invalid shape type");
    }
  }
Enter fullscreen mode Exit fullscreen mode

Specific shape interfaces (Circle, Rectangle) extend the base Shape interface and must have their type property set to the corresponding enum value.

This lets the calculateArea function use the type property as a discriminator to determine the appropriate calculation.

Example 5: Enums as Data Structures

This TypeScript code defines a simple model for representing playing cards, focusing on the suits, ranks, and the color of the card, which is derived from its suit.

It consists of two enums, a function to get a card’s numerical value, an interface to describe a card’s structure, and a function to create a card.

enum Suit {
  Hearts = "", // Red suit
  Diamonds = "", // Red suit
  Clubs = "", // Black suit
  Spades = "" // Black suit
}

enum Rank {
  Ace = 1,
  Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten,
  Jack, Queen, King 
}

function getCardValue(rank: Rank): number {
  if (rank <= Rank.Ten) {
    return rank;
  } else {
    return 10;
  }
}

interface Card {
  suit: Suit;
  rank: Rank;
  color: string; // Derived property based on suit
}

function createCard(suit: Suit, rank: Rank): Card {
  return {
    suit,
    rank,
    color: suit === Suit.Hearts || suit === Suit.Diamonds ? 'Red' : 'Black'
  }
}

// Usage
let card1 = createCard(Suit.Hearts, Rank.Ace);
console.log(`The Ace of Hearts is red: ${card1.color}`); // Output: The Ace of Hearts is red: Red

let card2 = createCard(Suit.Spades, Rank.Queen);
console.log(`The Queen of Spades is black: ${card2.color}`); // Output: The Queen of Spades is black: Black
Enter fullscreen mode Exit fullscreen mode

Function getCardValue takes a Rank as an argument and returns a number. For ranks Ace through Ten (numerically 1 to 10), it returns the rank's numeric value. For face cards (Jack, Queen, King), which have rank values greater than 10, it returns 10.

Two card objects are created using the createCard function: card1 as the Ace of Hearts (which is red) and card2 as the Queen of Spades (which is black).

This code is a straightforward way to model the basic properties of playing cards in a type-safe manner using TypeScript’s features like enums, interfaces, and type inference.

Conclusion

Enums provide a solid foundation for clean and structured TypeScript code. As your projects grow in complexity, their benefits will continue to shine.

TypeScript enums are a powerful tool to elevate your code. They enhance readability, maintainability, and type safety.

Check out my other TypeScript articles:

This article was originally posted on Medium.

Top comments (9)

Collapse
 
mrdulin profile image
official_dulin • Edited

The initializers here are not necessary. We could leave off the initializers entirely.

enum Movement {
  Up,
  Down,
  Left,
  Right
}
Enter fullscreen mode Exit fullscreen mode

Here, Up would have the value 0, Down would have 1, etc. This auto-incrementing behavior is useful for cases where we might not care about the member values themselves, but do care that each value is distinct from other values in the same enum.

But of course, you can still use String enums

Collapse
 
manishmandal profile image
Manish Mandal

I prefer using regular objects with "as const" to act like enums as it has more use cases

Collapse
 
alexefimenko profile image
Alex • Edited

Great advice, thanks!
Object like

const loadingState = {
  Idle: 'idle',
  Loading: 'loading',
  Success: 'success',
  Error: 'error',
} as const
Enter fullscreen mode Exit fullscreen mode

can work the same way as enums.
Could you mention the use cases where this approach is better?

Collapse
 
cristobalgvera profile image
Cristóbal Gajardo Vera • Edited

When working with Angular, if you define an enum to be used as an input property, it willl require you to assign that enum to a class attribute of the parent component and later use it in the html template, else you simply can't us it. An this is just an example.

In that case, a map using as const and also keyof typeof will let you to use the typed string in the html.

The approach that unify the definition for enum and let you use it as is, is the following:

const MyEnum = {
  FIRST: 'First',
  SECOND: 'Second',
} as const;

type MyEnum = (typeof MyEnum)[keyof typeof MyEnum];
      // ^? type MyEnum = "First" | "Second"

declare function func(a: MyEnum): void;

func(MyEnum.FIRST); // Valid
func('First'); // Valid
Enter fullscreen mode Exit fullscreen mode

The real problem is that TypeScript doesn't recognize an enum as a string, even if the enum values are all strings. This aspect of enum make them "weird" in constrast of as const objects.

enum MyEnum {
  FIRST = 'First',
  SECOND = 'Second',
};

declare function func(a: MyEnum): void;

func(MyEnum.FIRST); // Valid
func('First'); // Invalid
Enter fullscreen mode Exit fullscreen mode

A better explanation: dev.to/muszynov/something-about-ty...

Collapse
 
sunpietro profile image
Piotr Nalepa

You can change it to: const enum loadingState and it will work the same without being smarter giving as const at the end.

Collapse
 
luiztux profile image
Luiz Vicente

Forgive my ignorance about enums 🥺, but what would be a real use case instead of Object?

Collapse
 
_ndeyefatoudiop profile image
Ndeye Fatou Diop

I really like that in your examples the enumeration are named : which makes it so much better to log them (otherwise you get numbers) ☺️

Collapse
 
alexefimenko profile image
Alex

Thank you!
Hope you learned something new from the article 😊

Collapse
 
rijvimahmudd profile image
Rijvi Mahmud

I like these examples. it's easy to catch and memorize.