A type guard function's return type is written value is Type instead of a plain boolean — this special return type, called a type predicate, tells TypeScript that a true return narrows the checked value to Type everywhere the function is used as a condition. This extends narrowing beyond what typeof/instanceof/in can express to any arbitrarily complex runtime check.
Writing a custom type guard
The pet is Fish return type is what makes this special — a plain : boolean return would work at runtime identically, but wouldn't narrow anything for the type-checker.
type Fish = { swim: () => void };
type Bird = { fly: () => void };
function isFish(pet: Fish | Bird): pet is Fish { // the type predicate
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
pet.swim(); // narrowed to Fish, thanks to the type guard
} else {
pet.fly(); // narrowed to Bird
}
}Type guards with arrays: filtering out undefined
Without a type guard, .filter(x => x !== undefined) narrows the runtime values correctly but TypeScript's own inference still leaves the array type as (T | undefined)[] — a type guard fixes the type-level result to match reality.
const items: (string | undefined)[] = ["a", undefined, "b", undefined];
function isDefined<T>(value: T | undefined): value is T {
return value !== undefined;
}
const filtered: string[] = items.filter(isDefined);
// without the type guard, TypeScript would still see (string | undefined)[]Type guards for validating unknown data
This is one of the most practical real-world uses — safely narrowing genuinely unknown external data (a JSON API response, for instance) into a specific expected shape, with the check written once and reused everywhere that data is handled.
interface User { name: string; age: number; }
function isUser(data: unknown): data is User {
return (
typeof data === "object" && data !== null &&
"name" in data && "age" in data
);
}
const response: unknown = fetchApiResponse();
if (isUser(response)) {
console.log(response.name); // safely narrowed to User
}