TypeScript - Type Guards: A Beginner's Guide

Hello there, future coding superstar! ? I'm thrilled to be your guide on this exciting journey into the world of TypeScript and Type Guards. As someone who's been teaching computer science for many years, I can tell you that understanding type guards is like having a superpower in the TypeScript universe. So, let's dive in and unlock this power together!

TypeScript - Type Guards

What are Type Guards?

Before we jump into the specifics, let's understand what type guards are. Imagine you're a security guard at a fancy party. Your job is to check each guest's invitation and make sure they're allowed in the right areas. Type guards in TypeScript do a similar job - they help the compiler check and narrow down the type of a variable within a certain block of code.

Now, let's explore the three main types of guards we'll be learning about today:

Type Guard Description
typeof Checks the type of a variable
in Checks if a property exists in an object
instanceof Checks if an object is an instance of a class

The 'typeof' Type Guard in TypeScript

The 'typeof' type guard is like asking, "What kind of thing are you?" It's used to check the type of a variable. Let's look at an example:

function printAll(strs: string | string[] | null) {
  if (typeof strs === "object") {
    for (const s of strs) {
      console.log(s);
    }
  } else if (typeof strs === "string") {
    console.log(strs);
  } else {
    // do nothing
  }
}

In this example, we're using typeof to check if strs is an object (which includes arrays) or a string. If it's an object, we loop through it. If it's a string, we print it directly.

Here's a fun way to remember this: imagine you're at a pet shop, and you want to know if an animal is a dog or a cat. You might ask, "What type of animal are you?" That's exactly what typeof does in TypeScript!

The 'in' Type Guard in TypeScript

The 'in' type guard is like asking, "Do you have this feature?" It checks if a property exists in an object. Let's look at an example:

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function move(animal: Fish | Bird) {
  if ("swim" in animal) {
    animal.swim();
  } else {
    animal.fly();
  }
}

In this code, we're checking if the animal has a swim property. If it does, we assume it's a Fish and call the swim method. If not, we assume it's a Bird and call the fly method.

Think of it like this: if you're trying to figure out if your new pet is a fish or a bird, you might check if it has fins. That's what the 'in' type guard does - it checks for a characteristic property.

The 'instanceof' Type Guard in TypeScript

The 'instanceof' type guard is like asking, "Are you a member of this family?" It checks if an object is an instance of a particular class. Here's an example:

class Bird {
  fly() {
    console.log("Flying...");
  }
}

class Fish {
  swim() {
    console.log("Swimming...");
  }
}

function move(pet: Bird | Fish) {
  if (pet instanceof Bird) {
    pet.fly();
  } else {
    pet.swim();
  }
}

let myBird = new Bird();
move(myBird); // Output: Flying...

In this example, we're checking if pet is an instance of the Bird class. If it is, we call the fly method. If not, we assume it's a Fish and call the swim method.

Imagine you're at a family reunion, and you're trying to figure out if someone is part of the Johnson family. You might ask, "Are you a Johnson?" That's exactly what instanceof does in TypeScript!

Putting It All Together

Now that we've learned about these three type guards, let's see how we might use them all together:

class Car {
  drive() { console.log("Vroom!"); }
}

class Bicycle {
  ride() { console.log("Pedal!"); }
}

type Vehicle = Car | Bicycle | string;

function useVehicle(vehicle: Vehicle) {
  if (typeof vehicle === "string") {
    console.log(`The vehicle is: ${vehicle}`);
  } else if (vehicle instanceof Car) {
    vehicle.drive();
  } else if ("ride" in vehicle) {
    vehicle.ride();
  } else {
    console.log("Unknown vehicle type");
  }
}

useVehicle("Skateboard");  // Output: The vehicle is: Skateboard
useVehicle(new Car());     // Output: Vroom!
useVehicle(new Bicycle()); // Output: Pedal!

In this example, we're using all three type guards:

  1. We use typeof to check if the vehicle is a string.
  2. We use instanceof to check if it's a Car.
  3. We use in to check if it has a ride method (which would indicate it's a Bicycle).

This is like being a super detective, using all your skills to figure out exactly what kind of vehicle you're dealing with!

Conclusion

And there you have it, my coding apprentices! We've journeyed through the land of TypeScript Type Guards, exploring the typeof, in, and instanceof guards. These powerful tools will help you write safer, more predictable code by allowing TypeScript to understand your intentions better.

Remember, using type guards is like having a friendly conversation with your code. You're simply asking it questions to understand it better. So next time you're coding, don't be shy - strike up a chat with your variables using these type guards!

Keep practicing, stay curious, and before you know it, you'll be guarding types like a pro. Until next time, happy coding! ??

Credits: Image by storyset