Asher Cohen
Back to posts

TypeScript Predicate Type Guards

Narrow union types with custom type predicates for safer, more expressive code

Introduction

Type predicates are a powerful TypeScript feature that lets you create custom type guards. They enable you to narrow union types based on runtime checks, providing type safety throughout your codebase.

What is a Type Predicate?

A type predicate is a function whose return type is a special kind of type assertion: parameterName is Type.

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

The pet is Fish is our type predicate. It tells TypeScript: "If this function returns true, then pet is definitely a Fish."

How Type Predicates Work

When you use a type predicate in a conditional, TypeScript narrows the type within that block.

let pet = getSmallPet();

if (isFish(pet)) {
  pet.swim(); // TypeScript knows pet is Fish
} else {
  pet.fly(); // TypeScript knows pet is Bird
}

Practical Examples

Property Checks

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

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

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

Type Narrowing with typeof

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

function process(value: string | number) {
  if (isString(value)) {
    return value.toUpperCase();
  }
  return value.toFixed(2);
}

Array Filtering

function isDefined<T>(value: T | null | undefined): value is T {
  return value !== null && value !== undefined;
}

const values: (string | null | undefined)[] = ['hello', null, 'world'];
const filtered = values.filter(isDefined); // Type: string[]

Best Practices

  • Always perform actual runtime checks
  • Use unknown for input parameters
  • Keep predicates simple and focused
  • Document complex predicates

Conclusion

Type predicates bridge compile-time types and runtime checks, enabling safer TypeScript code.

#typescript #types #type-safety