BeCuriouzLearn something new every day
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.
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.