TypeScript - Enums: A Beginner's Guide

Hello, future programmers! Today, we're going to dive into the wonderful world of TypeScript Enums. Don't worry if you've never coded before – I'll be your friendly guide through this journey, just like I've been for countless students over my years of teaching. So, let's get started!

TypeScript - Enums

What are Enums?

Before we jump into the different types of Enums, let's understand what they are. Imagine you're organizing your closet. You might have different categories for your clothes: shirts, pants, shoes, etc. Enums in TypeScript are like these categories – they help us group related values together and give them meaningful names.

Numeric Enums

Numeric Enums are the most common type of Enums in TypeScript. They're like assigning numbers to your favorite superheroes.

enum Superheroes {
    Superman,
    Batman,
    WonderWoman,
    Flash
}

console.log(Superheroes.Superman);  // Output: 0
console.log(Superheroes.Batman);    // Output: 1
console.log(Superheroes.WonderWoman);  // Output: 2
console.log(Superheroes.Flash);     // Output: 3

In this example, TypeScript automatically assigns numbers starting from 0. Superman gets 0, Batman gets 1, and so on. It's like they're lining up for a group photo, and we're giving them numbers based on their position!

But what if we want to start with a different number? No problem! We can do that too:

enum Villains {
    Joker = 1,
    LexLuthor,
    Cheetah,
    ReverseFlash
}

console.log(Villains.Joker);        // Output: 1
console.log(Villains.LexLuthor);    // Output: 2
console.log(Villains.Cheetah);      // Output: 3
console.log(Villains.ReverseFlash); // Output: 4

Here, we told TypeScript to start counting from 1 for Joker, and it automatically continued from there for the rest.

String Enums

Now, let's say you're not a fan of numbers and prefer words. That's where String Enums come in handy. They're like giving your pets nicknames!

enum PetNames {
    Dog = "BUDDY",
    Cat = "WHISKERS",
    Fish = "BUBBLES",
    Bird = "TWEETY"
}

console.log(PetNames.Dog);   // Output: "BUDDY"
console.log(PetNames.Cat);   // Output: "WHISKERS"
console.log(PetNames.Fish);  // Output: "BUBBLES"
console.log(PetNames.Bird);  // Output: "TWEETY"

With String Enums, we have to give a value to each member. It's like making sure each of your pets knows their special nickname!

Heterogeneous Enums

Sometimes, life isn't just numbers or just strings – it's a mix! Heterogeneous Enums are like a box of assorted chocolates – you get a bit of everything.

enum MixedBag {
    Number = 1,
    String = "STRING",
    AnotherNumber = 2
}

console.log(MixedBag.Number);        // Output: 1
console.log(MixedBag.String);        // Output: "STRING"
console.log(MixedBag.AnotherNumber); // Output: 2

While this is possible, it's generally recommended to stick to either all numbers or all strings in an Enum for consistency.

Enums at Runtime

One cool thing about Enums is that they exist at runtime. This means you can use them in your code just like any other object!

enum Fruits {
    Apple,
    Banana,
    Orange
}

function getFruitName(fruit: Fruits): string {
    return Fruits[fruit];
}

console.log(getFruitName(Fruits.Apple));  // Output: "Apple"
console.log(getFruitName(Fruits.Banana)); // Output: "Banana"

Here, we're using the Enum as if it were a regular JavaScript object. It's like having a magical dictionary that can translate numbers to fruit names!

Enums at Compile Time

Enums also shine at compile time. They help TypeScript catch errors before your code even runs!

enum DaysOfWeek {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}

let day: DaysOfWeek = DaysOfWeek.Monday;
day = DaysOfWeek.Friday;  // This is fine

// day = "Monday";  // Error: Type '"Monday"' is not assignable to type 'DaysOfWeek'.

TypeScript will give you an error if you try to assign a value that's not part of the Enum. It's like having a strict bouncer at a club who only lets in the days of the week!

Ambient Enums

Ambient Enums are a bit special. They're used when you're telling TypeScript about Enums that exist somewhere else in your code or in a library you're using.

declare enum ExternalEnum {
    A = 1,
    B,
    C = 2
}

The declare keyword is like saying, "Hey TypeScript, trust me, this Enum exists somewhere else in the code." It's useful when you're working with code that's not written in TypeScript.

Object vs. Enums

You might be wondering, "Why not just use objects?" Well, Enums have some advantages:

// Using an object
const ColorObject = {
    Red: 'RED',
    Green: 'GREEN',
    Blue: 'BLUE'
} as const;

// Using an Enum
enum ColorEnum {
    Red = 'RED',
    Green = 'GREEN',
    Blue = 'BLUE'
}

// With objects, you need to use type assertions
let objColor: keyof typeof ColorObject = 'Red';

// With Enums, it's more straightforward
let enumColor: ColorEnum = ColorEnum.Red;

Enums provide better type safety and are more concise in many cases.

Using Enum as a Function Parameter

Enums are great for function parameters. They make your code more readable and prevent errors:

enum Sizes {
    Small,
    Medium,
    Large
}

function orderCoffee(size: Sizes): string {
    switch(size) {
        case Sizes.Small:
            return "Here's your small coffee!";
        case Sizes.Medium:
            return "One medium coffee coming right up!";
        case Sizes.Large:
            return "Large coffee, extra caffeine!";
        default:
            return "We don't have that size!";
    }
}

console.log(orderCoffee(Sizes.Medium));  // Output: "One medium coffee coming right up!"
// console.log(orderCoffee("Venti"));    // Error: Argument of type '"Venti"' is not assignable to parameter of type 'Sizes'.

By using an Enum for the size parameter, we ensure that only valid sizes can be passed to the function. It's like having a menu with fixed options – no confusion about what sizes are available!

Enum Methods

Here's a table of useful methods for working with Enums:

Method Description Example
Object.keys() Get all the keys of an Enum Object.keys(Sizes)
Object.values() Get all the values of an Enum Object.values(Sizes)
Object.entries() Get key-value pairs of an Enum Object.entries(Sizes)
Enum[key] Get the value for a key Sizes[Sizes.Small]
Enum[value] Get the key for a value (for number Enums) Sizes[0]

These methods help you work with Enums in different ways, like getting all the available sizes or finding the name of a size from its value.

And there you have it! You've just taken your first steps into the world of TypeScript Enums. Remember, programming is like learning a new language – it takes practice, but soon you'll be fluent in Enum-speak! Keep coding, stay curious, and don't forget to have fun along the way. Who knows, maybe you'll create an Enum for your favorite ice cream flavors next!

Credits: Image by storyset