TypeScript Type Guards

Runtime checks for type safety

🛡️ What are Type Guards?

Type Guards are runtime checks that help TypeScript understand and narrow down the specific type of a variable within a conditional block, ensuring type safety during code execution.


// Simple type guard example
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

if (isString(input)) {
  console.log(input.toUpperCase()); // TypeScript knows it's a string
}
                                    

Output:

✅ Type safely checked and narrowed

Key Type Guard Concepts

🔍

typeof Guard

Check primitive types at runtime

if (typeof x === 'string') {
  x.toUpperCase();
}
🏗️

instanceof Guard

Check class instances

if (obj instanceof Date) {
  obj.getTime();
}

Custom Guards

Create your own type predicates

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

in Operator

Check property existence

if ('name' in obj) {
  console.log(obj.name);
}

🔹 typeof Type Guard

Use typeof to check primitive types:

function processValue(value: string | number) {
  if (typeof value === 'string') {
    // TypeScript knows value is string here
    console.log(value.toUpperCase());
  } else {
    // TypeScript knows value is number here
    console.log(value.toFixed(2));
  }
}

processValue('hello');  // Output: HELLO
processValue(42.567);   // Output: 42.57

// Multiple type checks
function printValue(val: string | number | boolean) {
  if (typeof val === 'string') {
    console.log(`String: ${val}`);
  } else if (typeof val === 'number') {
    console.log(`Number: ${val}`);
  } else {
    console.log(`Boolean: ${val}`);
  }
}

Output:

HELLO

42.57

🔹 instanceof Type Guard

Check if an object is an instance of a class:

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

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

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();  // TypeScript knows it's a Dog
  } else {
    animal.meow();  // TypeScript knows it's a Cat
  }
}

const myDog = new Dog();
const myCat = new Cat();

makeSound(myDog);  // Output: Woof!
makeSound(myCat);  // Output: Meow!

Output:

Woof!

Meow!

🔹 Custom Type Guards

Create custom type predicates with "is" keyword:

interface Fish {
  swim: () => void;
}

interface Bird {
  fly: () => void;
}

// Custom type guard function
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function movePet(pet: Fish | Bird) {
  if (isFish(pet)) {
    pet.swim();  // TypeScript knows it's Fish
  } else {
    pet.fly();   // TypeScript knows it's Bird
  }
}

// Another example
interface User {
  name: string;
  email: string;
}

function isUser(obj: any): obj is User {
  return obj && typeof obj.name === 'string' && typeof obj.email === 'string';
}

🔹 in Operator Type Guard

Check if a property exists in an object:

type Admin = {
  name: string;
  privileges: string[];
};

type Employee = {
  name: string;
  startDate: Date;
};

type UnknownEmployee = Admin | Employee;

function printEmployeeInfo(emp: UnknownEmployee) {
  console.log(`Name: ${emp.name}`);
  
  if ('privileges' in emp) {
    console.log(`Privileges: ${emp.privileges.join(', ')}`);
  }
  
  if ('startDate' in emp) {
    console.log(`Start Date: ${emp.startDate.toDateString()}`);
  }
}

const admin: Admin = {
  name: 'John',
  privileges: ['create-server', 'delete-user']
};

printEmployeeInfo(admin);

Output:

Name: John

Privileges: create-server, delete-user

🔹 Discriminated Unions

Use a common property to distinguish types:

interface Circle {
  kind: 'circle';
  radius: number;
}

interface Square {
  kind: 'square';
  sideLength: number;
}

interface Rectangle {
  kind: 'rectangle';
  width: number;
  height: number;
}

type Shape = Circle | Square | Rectangle;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
    case 'square':
      return shape.sideLength ** 2;
    case 'rectangle':
      return shape.width * shape.height;
  }
}

const myCircle: Circle = { kind: 'circle', radius: 5 };
console.log(getArea(myCircle));  // Output: 78.54

Output:

78.54

🧠 Test Your Knowledge

Which keyword is used in custom type guard functions?