TypeScript Type Guards

Type guards are expressions that let TypeScript narrow a broad type down to a more specific one at particular points in your code, based on runtime checks like typeof, instanceof, in, and custom predicate functions.

#Jobseeker

Find matching jobs & Auto apply with AI

Hyring
Jobseeker using Hyring to search, apply, and prepare with AI

typeof Guards for Primitives

Inside an if statement, checking typeof value === "string" (or "number", "boolean", etc.) narrows a union of primitives to just the matching branch for the rest of that block.

Narrowing a primitive union

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

console.log(formatValue("  hi  ")); // "hi"
console.log(formatValue(3.14159));  // "3.14"

instanceof Guards for Class Instances

instanceof checks whether a value was constructed by a particular class, narrowing a union of class instances to the matching subclass inside the guarded branch.

Narrowing a class union

class ApiError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
  }
}

function handle(error: Error | ApiError): void {
  if (error instanceof ApiError) {
    console.log(`API error ${error.statusCode}: ${error.message}`);
  } else {
    console.log(`Generic error: ${error.message}`);
  }
}

handle(new ApiError(404, "Not found"));
handle(new Error("Something broke"));

in Guards and Custom Type Predicates

The in operator checks whether a property exists on an object, which is useful for distinguishing plain object shapes that don't share a class hierarchy. For reusable or more complex checks, write a custom function whose return type is 'param is Type' — a type predicate.

Custom guard built on in

interface Fish { swim(): void; }
interface Bird { fly(): void; }

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

function move(pet: Fish | Bird): void {
  if (isFish(pet)) {
    pet.swim(); // narrowed to Fish
  } else {
    pet.fly();  // narrowed to Bird
  }
}
  • typeof — narrows unions of primitive types like string, number, boolean
  • instanceof — narrows unions of class instances by constructor
  • in — narrows plain object unions by checking property presence
  • custom 'x is Type' predicates — reusable, composable guards for complex logic
  • discriminated unions with a literal 'kind' field — an alternative that plays well with switch statements
GuardCheck expressionTypical use
typeoftypeof x === "string"primitive unions
instanceofx instanceof MyClassclass instance unions
in"prop" in xplain object shape unions
custom predicateisX(x): x is Xreusable or multi-condition checks
Note: Pair custom type guards with discriminated unions and a switch on the literal 'kind' field, then add a default case that assigns the value to a variable typed never — the compiler will flag any unhandled case as an error.

Exercise: TypeScript Type Guards

Which operator narrows a union based on primitive types like 'string' or 'number'?