TypeScript Type Guards

TypeScript tutorial

🛡️ TypeScript Type Guards (Beginner → Advanced)

Type Guards in TypeScript are techniques that help the compiler narrow down types within a block of code.
They are essential for working with union types, APIs, and real-world applications like React & Node.js.


1️⃣ What Are Type Guards?

A type guard is an expression or function that performs a runtime check to guarantee the type of a variable.

if (typeof x === "string") {
// x is string here
}

✔ Safer code
✔ Fewer runtime errors
✔ Better IntelliSense


2️⃣ typeof Type Guard ⭐ (Primitive Types)

Used for primitive types.

function print(value: number | string) {
if (typeof value === "number") {
console.log(value.toFixed(2));
} else {
console.log(value.toUpperCase());
}
}

✔ Works with: string, number, boolean, bigint, symbol, undefined, function


3️⃣ instanceof Type Guard ⭐ (Classes)

Used with class instances.

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

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

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

✔ Works only with classes, not interfaces


4️⃣ in Operator Type Guard ⭐

Checks if a property exists in an object.

type Admin = {
role: "admin";
permissions: string[];
};

type User = {
role: "user";
email: string;
};

function check(person: Admin | User) {
if ("permissions" in person) {
console.log("Admin");
} else {
console.log("User");
}
}

✔ Very useful for object unions


5️⃣ Discriminated (Tagged) Unions ⭐⭐

A powerful and recommended pattern.

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

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

type Shape = Circle | Square;

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

✔ Clean
✔ Safe
✔ Exhaustive


6️⃣ Custom Type Guards (is) ⭐⭐

Define your own type guard functions.

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

function isFish(pet: Fish | Bird): pet is Fish {
return "swim" in pet;
}

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

✔ Extremely powerful
✔ Common in enterprise code


7️⃣ Type Guards with unknown

function handle(value: unknown) {
if (typeof value === "string") {
console.log(value.toUpperCase());
}
}

✔ Required before using unknown


8️⃣ Exhaustiveness Checking with never 🔥

function assertNever(x: never): never {
throw new Error("Unexpected object");
}

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

✔ Ensures all cases handled
✔ Great for refactoring safety


9️⃣ Type Guards in Arrays ⭐

const values: (string | number)[] = [1, "a", 2, "b"];

const numbers = values.filter(
(v): v is number => typeof v === "number"
);

✔ Useful for API data cleanup


🔟 Common Mistakes ❌

  • Using any instead of guards

  • Forgetting to narrow unknown

  • Using instanceof with interfaces

  • Overusing type assertions (as)


📌 Interview Questions (Must Prepare)

  1. What is a type guard?

  2. Difference between typeof and instanceof

  3. What is a custom type guard?

  4. What is a discriminated union?

  5. Why is never used in guards?

  6. How to narrow unknown?


🔥 Real-World Use Cases

  • API response validation

  • Form handling

  • Redux reducers

  • Event handlers

  • Library design


✅ Summary

✔ Type guards narrow union types safely
✔ Built-in: typeof, instanceof, in
✔ Discriminated unions are best practice
✔ Custom guards provide maximum control
✔ Essential for TypeScript mastery & interviews

You may also like...