Functions Whose Return Type Tells TS How to Narrow
Predicate Functions
A function with return type `x is T` is a user-defined type guard. Functions with `asserts` go further — they refine the type or throw.
What you'll learn
- Author a predicate function
- Use `asserts` for invariants
- Recognize the use in `.filter`
A predicate function tells TS how to narrow a type. Two flavors —
returning x is T (narrows after if) and asserts x is T
(narrows for the rest of the scope).
x is T — Narrowing After a Check
function isString(x: unknown): x is string {
return typeof x === "string";
}
function describe(x: unknown) {
if (isString(x)) {
x.toUpperCase(); // ✓ x is string
}
} Inside the if, TS treats x as string. Covered in the
predicates lesson earlier.
asserts x is T — Narrowing for the Rest
function assertIsString(x: unknown): asserts x is string {
if (typeof x !== "string") {
throw new Error("Not a string");
}
}
function describe(x: unknown) {
assertIsString(x);
x.toUpperCase(); // ✓ x is string for the rest of the function
} Difference: after calling an asserts function, the narrowed type
applies for the rest of the scope, not just inside an if.
The function must either:
- Throw if the assertion fails
- Or return normally (in which case the type is true)
A Pattern — Invariant Checks
function assertDefined<T>(value: T | null | undefined): asserts value is T {
if (value == null) {
throw new Error("Expected value to be defined");
}
}
function process(user: User | null) {
assertDefined(user);
console.log(user.name); // ✓ user is User
} A nice replacement for if (!user) throw new Error(...) —
single-line, type-aware.
Predicates in .filter
The most popular use:
const items = ["a", null, "b", null];
const filtered = items.filter((x): x is string => x !== null);
// filtered: string[] Without the predicate, the type stays (string | null)[] — TS
can’t prove the filter actually removed nulls.
The Trust Caveat
Predicates and asserts trust your implementation. TS doesn’t verify the runtime behavior matches the type-level claim. Treat them as part of your public API and test them.
End of Chapter
That wraps functions and inference. Next chapter — generics, the single biggest leap in TypeScript power.
Generics Intro →