~/hackweb.dev
TypeScript Type Guards & Narrowing
Quiz
...

TypeScript Type Guards & Narrowing

beginner · updated Tue Sep 08 2026Contribute

Master type guards, instanceof, and type narrowing techniques.

TypeScript Type Guards & Narrowing

Type guards narrow union types to specific types, enabling type-safe operations on them.

typeof Guard

typeof narrows primitive types:

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

instanceof Guard

instanceof narrows based on class constructors:

class Dog { bark(): void {} }
class Cat { meow(): void {} }

function handleAnimal(animal: Dog | Cat): void {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.meow();
  }
}

Discriminated Unions

A literal property acts as a discriminator for automatic narrowing:

type Circle = { kind: "circle"; radius: number };
type Rectangle = { kind: "rectangle"; width: number; height: number };
type Shape = Circle | Rectangle;

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

Tip: Discriminated unions are the cleanest narrowing pattern.

Custom Type Guards

Use value is Type return type to create reusable guards:

function isString(value: unknown): value is string {
  return typeof value === "string";
}

let value: unknown = "hello";
if (isString(value)) {
  console.log(value.toUpperCase());
}

Truthiness and in Guards

Truthiness narrows null/undefined. in checks for property existence:

function process(value: string | null): void {
  if (value) {
    console.log(value.toUpperCase());
  }
}

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

Best Practices

  • Use discriminated unions for the cleanest type narrowing
  • Prefer switch statements to handle all cases
  • Use custom type guards for complex type checking
  • Keep type guards simple — avoid excessive nesting
  • Use truthiness checks for null/undefined narrowing

Common Mistakes

  • Not using discriminated unions when possible
  • Using any instead of type guards
  • Forgetting a default case in switch statements
  • Confusing type guards with type assertions — guards narrow, assertions override
  • Not using value is Type in custom guard return types