BeCuriouzBeCuriouz

Learn something new every day

🔷 TypeScript
🛂

Type Guards You Write Yourself: the x is T Predicate

💡 A function returning arg is Type becomes a reusable, custom narrowing tool — but TypeScript trusts its logic blindly, so a wrong predicate silently breaks type safety.

In earlier cards you narrowed types with built-in checks like typeof, instanceof, and in. But those don't help when the test is custom — say, checking whether an unknown object is really a User. That's where a user-defined type guard comes in: an ordinary function whose return type is written as arg is Type instead of boolean.

This special return type is called a type predicate. At runtime the function just returns a boolean like any other, but at compile time TypeScript treats a true result as proof that the argument is that type — and narrows it inside the if branch.

ts
type Cat = { meow: () => void };
type Dog = { bark: () => void };

function isCat(pet: Cat | Dog): pet is Cat {
  return 'meow' in pet;
}

function speak(pet: Cat | Dog) {
  if (isCat(pet)) {
    pet.meow(); // narrowed to Cat here
  } else {
    pet.bark(); // narrowed to Dog here
  }
}

A crucial catch: TypeScript trusts your predicate without checking it. If isCat returned 'bark' in pet by mistake, the compiler would still narrow to Cat and you'd get a runtime crash — the is clause is a promise you are responsible for keeping. Predicates are especially powerful with Array.prototype.filter: typing a callback as (x): x is string lets arr.filter(isString) return a string[] instead of the wider original type.

🛂 Type Guards You Write Yourself: the x is T Predicate — BeCuriouz