Enums (short for "enumerations") are a feature in TypeScript that allows you to define a set of named constants. Enums make it easier to document intent or create a set of distinct cases. TypeScript provides both numeric and string-based enums.

Key Concepts

Numeric Enums

Numeric enums are the default in TypeScript. They are a set of named constants where each member is assigned a numeric value.

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

In this example:

  • Up is initialized with 1.
  • Down, Left, and Right are auto-incremented from 1.

String Enums

String enums are similar to numeric enums, but their values are strings instead of numbers.

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

String enums are useful when the meaning of the value is more important than the numeric value.

Heterogeneous Enums

Enums can also mix string and numeric members, though this is less common.

enum BooleanLikeHeterogeneousEnum {
    No = 0,
    Yes = "YES"
}

Computed and Constant Members

Enum members can be constant or computed. Constant members are evaluated at compile time, while computed members are evaluated at runtime.

enum FileAccess {
    None,
    Read = 1 << 1,
    Write = 1 << 2,
    ReadWrite = Read | Write,
    G = "123".length
}

In this example:

  • None is 0.
  • Read is 2 (1 shifted left by 1 bit).
  • Write is 4 (1 shifted left by 2 bits).
  • ReadWrite is 6 (bitwise OR of Read and Write).
  • G is 3 (length of the string "123").

Practical Examples

Using Enums in Functions

Enums can be used to make function parameters more readable and type-safe.

enum Direction {
    Up,
    Down,
    Left,
    Right
}

function move(direction: Direction) {
    switch (direction) {
        case Direction.Up:
            console.log("Moving up");
            break;
        case Direction.Down:
            console.log("Moving down");
            break;
        case Direction.Left:
            console.log("Moving left");
            break;
        case Direction.Right:
            console.log("Moving right");
            break;
    }
}

move(Direction.Up); // Output: Moving up

Reverse Mapping

Numeric enums in TypeScript support reverse mapping, which means you can access the name of an enum member from its value.

enum Direction {
    Up,
    Down,
    Left,
    Right
}

console.log(Direction[0]); // Output: Up
console.log(Direction["Up"]); // Output: 0

Enum as a Type

Enums can also be used as types to ensure that variables can only hold one of the enum's values.

enum Direction {
    Up,
    Down,
    Left,
    Right
}

let currentDirection: Direction = Direction.Up;
currentDirection = Direction.Left; // Valid
// currentDirection = 2; // Error: Type '2' is not assignable to type 'Direction'

Exercises

Exercise 1: Basic Enum Usage

Define an enum Color with members Red, Green, and Blue. Write a function getColorName that takes a Color and returns the name of the color as a string.

enum Color {
    Red,
    Green,
    Blue
}

function getColorName(color: Color): string {
    switch (color) {
        case Color.Red:
            return "Red";
        case Color.Green:
            return "Green";
        case Color.Blue:
            return "Blue";
        default:
            return "Unknown";
    }
}

// Test the function
console.log(getColorName(Color.Red)); // Output: Red
console.log(getColorName(Color.Green)); // Output: Green
console.log(getColorName(Color.Blue)); // Output: Blue

Exercise 2: String Enums

Define a string enum Status with members Pending, InProgress, and Completed. Write a function getStatusMessage that takes a Status and returns a message string.

enum Status {
    Pending = "PENDING",
    InProgress = "IN_PROGRESS",
    Completed = "COMPLETED"
}

function getStatusMessage(status: Status): string {
    switch (status) {
        case Status.Pending:
            return "The task is pending.";
        case Status.InProgress:
            return "The task is in progress.";
        case Status.Completed:
            return "The task is completed.";
        default:
            return "Unknown status.";
    }
}

// Test the function
console.log(getStatusMessage(Status.Pending)); // Output: The task is pending.
console.log(getStatusMessage(Status.InProgress)); // Output: The task is in progress.
console.log(getStatusMessage(Status.Completed)); // Output: The task is completed.

Common Mistakes and Tips

  • Using Enums for Constants: Enums are great for defining a set of related constants, but for unrelated constants, consider using const or readonly properties.
  • String vs Numeric Enums: Use string enums when the meaning of the value is more important than the numeric value. Numeric enums are useful when you need reverse mapping or bitwise operations.
  • Default Values: Remember that numeric enums start at 0 by default unless specified otherwise.

Conclusion

Enums in TypeScript provide a powerful way to define a set of named constants, making your code more readable and maintainable. They can be numeric, string-based, or even a mix of both. Understanding how to use enums effectively will help you write more robust and type-safe TypeScript code. In the next section, we will explore Type Inference, which allows TypeScript to automatically determine the types of variables and expressions.

© Copyright 2024. All rights reserved