3️⃣ 🧪 TypeScript Core & Special Types
Estimated reading: 4 minutes 32 views

🛡️ TypeScript – Type Guards Explained with Examples & Best Practices

🧲 Introduction – What Are Type Guards in TypeScript?

In TypeScript, type guards are expressions or functions that narrow down the type of a variable within a conditional block. They help the TypeScript compiler understand the actual runtime type of a value, improving type safety and enabling precise code paths.

🎯 In this guide, you’ll learn:

  • What type guards are and why they’re useful
  • Different types of type guards: typeof, instanceof, custom guards, etc.
  • How to write your own type guard functions
  • Real-world examples and best practices

🧪 What Is a Type Guard?

A type guard is a conditional check that tells TypeScript:

“Inside this block, this variable has this specific type.”

📌 Example:

function printLength(value: string | number) {
  if (typeof value === "string") {
    console.log(value.length); // ✅ Safe: value is string here
  } else {
    console.log(value.toFixed(2)); // ✅ Safe: value is number here
  }
}

Explanation:

  • TypeScript narrows the union type (string | number) to the correct type inside each branch.

🔤 1. typeof Type Guard

Used to check primitive types like string, number, boolean, undefined, and symbol.

✅ Example:

function formatValue(val: string | number) {
  if (typeof val === "string") {
    return val.toUpperCase();
  }
  return val.toFixed(2);
}

📌 Supported Types:

  • "string"
  • "number"
  • "boolean"
  • "undefined"
  • "symbol"
  • "bigint"
  • "function"

🧱 2. instanceof Type Guard

Used to check if a value is an instance of a class or constructor function.

✅ Example:

class Dog {
  bark() {
    console.log("Woof!");
  }
}

class Cat {
  meow() {
    console.log("Meow!");
  }
}

function makeSound(pet: Dog | Cat) {
  if (pet instanceof Dog) {
    pet.bark();
  } else {
    pet.meow();
  }
}

Explanation:

  • TypeScript understands which class the object belongs to based on the instanceof check.

🧩 3. Discriminated Unions (Tagged Unions)

A type guard using a common discriminant property (often a string literal).

✅ Example:

type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };

type Shape = Circle | Square;

function area(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "square":
      return shape.side ** 2;
  }
}

Explanation:

  • kind acts as a tag that differentiates the object type.
  • Ideal for APIs and reducers with multiple shape types.

🧠 4. Custom Type Guard Functions

You can define your own type guard function using a special return type:
value is Type

✅ Example:

type Admin = { role: "admin"; accessLevel: number };
type Guest = { role: "guest"; expiresIn: number };

type User = Admin | Guest;

function isAdmin(user: User): user is Admin {
  return user.role === "admin";
}

function checkAccess(user: User) {
  if (isAdmin(user)) {
    console.log(`Admin access level: ${user.accessLevel}`);
  } else {
    console.log(`Guest expires in: ${user.expiresIn}`);
  }
}

Explanation:

  • isAdmin is a custom guard that narrows user to Admin inside the if block.
  • Useful for complex checks that can’t be handled with typeof or instanceof.

🧼 5. Truthy Guards (Caution Required)

TypeScript also narrows types based on truthiness checks, but this can be error-prone.

✅ Example:

function showLength(text?: string) {
  if (text) {
    console.log(text.length); // text is narrowed to string
  }
}

⚠️ Be careful: 0, "", false, and null are also falsy.


🔒 6. in Operator for Property Checking

You can use "property" in obj to confirm if an object has a property.

✅ Example:

type Car = { model: string; engine: string };
type Bicycle = { model: string; gears: number };

type Vehicle = Car | Bicycle;

function describe(vehicle: Vehicle) {
  if ("engine" in vehicle) {
    console.log(`Car engine: ${vehicle.engine}`);
  } else {
    console.log(`Bicycle gears: ${vehicle.gears}`);
  }
}

⚠️ Common Mistakes & How to Avoid Them

❌ Mistake✅ Solution
Using typeof on non-primitive typesUse instanceof or custom type guards
Forgetting to use user is TypeAlways use return type annotation for custom guards
Trusting truthy checks blindlyUse explicit type checks when needed

💡 Best Practices

  • ✅ Use typeof for primitive checks
  • ✅ Use instanceof for class-based logic
  • ✅ Prefer custom type guards for advanced logic
  • ✅ Use discriminated unions for scalable type-safe APIs
  • ❌ Don’t rely only on truthy/falsy checks for narrowing

📌 Summary – Recap & Next Steps

Type guards are essential for writing robust, type-safe logic in TypeScript. They allow you to safely work with union types, custom objects, and conditional flows.

🔍 Key Takeaways:

  • Type guards narrow a variable’s type within a scope
  • Use typeof, instanceof, in, or discriminants for safe checks
  • Build custom type guards with value is Type return
  • Avoid relying solely on truthy/falsy logic

⚙️ Real-world relevance: Type guards power form validation, API parsing, DOM checks, and business logic in modern TypeScript applications.


❓ FAQs – Type Guards in TypeScript

❓ Can I use type guards on union types?
✅ Yes, that’s the main use case—to narrow a union type to a specific subtype.

❓ Is typeof valid for custom classes?
❌ No. Use instanceof for class checks, not typeof.

❓ Can I use type guards in arrow functions?
✅ Yes. Just make sure to specify the return type like:

const isString = (x: any): x is string => typeof x === "string";

❓ Do type guards exist in JavaScript?
🧠 No. They are a TypeScript-specific feature built on top of JavaScript logic.


Share Now :

Leave a Reply

Your email address will not be published. Required fields are marked *

Share

TypeScript — Type Guards

Or Copy Link

CONTENTS
Scroll to Top