Type Predicates & in / instanceof
Beginner · 20 min read · ▶ live playground · ✦ checkpoint
A type predicate is a function return type like pet is Fish that tells TypeScript, "if this function returns true, narrow that value to this type." Use predicates when normal narrowing needs a name, and use them carefully: the checker trusts the promise you write.
You already have clean tags from discriminated unions. This lesson is for messier cases: class instances, objects that only differ by fields, and checks you want to reuse.
instanceof checks runtime constructors
A class is JavaScript syntax for making objects that share a constructor. The instanceof operator is a JavaScript check that asks whether an object was created by a class or built-in constructor.
instanceof works because OnlineWorkshop, ClassroomWorkshop, and Date exist at runtime. Type aliases do not. Types erase, so there is no Workshop object for JavaScript to check.
Use instanceof when your union members are real class instances or built-in constructors, JavaScript constructors provided by the language like Date and Error. For plain object shapes, reach for a structural check instead.
in checks object structure
A structural check is a runtime check that looks for an object's properties instead of its class. The in operator is JavaScript syntax that checks whether a property key exists in an object.
Before the if, lead.email is unsafe because PhoneLead does not have that field. After "email" in lead, TypeScript narrows lead to EmailLead.
This is the same control-flow analysis from 4.2. The difference is the kind of evidence. typeof checks primitive values. instanceof checks constructors. in checks object structure.
Name a check with pet is Fish
A user-defined type guard is a function that performs a runtime check and returns a type predicate. The return type names the narrowed type:
Read pet is Fish out loud: "this function tells the checker whether pet is a Fish." The function still returns a boolean at runtime. The special return type is for the checker.
This is useful when the same check appears in several places, or when the condition is long enough that naming it makes the code easier to read.
type Fish = { name: string; swim: () => string };
type Bird = { name: string; fly: () => string };
type Pet = Fish | Bird;
function isFish(pet: Pet): pet is Fish {
return "fly" in pet;
}
function play(pet: Pet): string {
if (isFish(pet)) {
return pet.swim();
}
return pet.fly();
}
const robin: Pet = {
name: "Robin",
fly: () => "flap",
};
console.log(play(robin));→ TypeError: pet.swim is not a function
This is the same lesson as annotations: a type predicate is a promise you make. The checker holds later code to that promise, but it cannot make the promise true.
First look: assertion functions
An assertion function is a function whose return type uses asserts to tell TypeScript that it throws unless a condition is true. It is like a type guard that does not return true or false; it either returns normally with a narrowed value, or it stops the program.
This is only a first look. Later, assertion lessons will spend more time on how to make these honest.
type StaffMember = {
name: string;
role: "teacher" | "mentor";
};
function assertStaffMember(value: unknown): asserts value is StaffMember {
if (typeof value !== "object" || value === null) {
throw new Error("Not a staff member");
}
const record = value as Record<string, unknown>;
if (
typeof record.name !== "string" ||
(record.role !== "teacher" && record.role !== "mentor")
) {
throw new Error("Not a staff member");
}
}
const incoming: unknown = { name: "Maya", role: "mentor" };
assertStaffMember(incoming);
console.log(incoming.name.toUpperCase() + " can open staff tools");→ MAYA can open staff tools
Record<string, unknown> means an object with string keys and values you still have to check. The narrow as Record<string, unknown> line lets this guard inspect properties after the object check. It is not the proof by itself. The proof is the typeof and equality checks that follow.
Now you have the full Section 4 toolkit: unions, literal tags, narrowing, discriminated unions, reusable predicates, and never checklists. Project 1 asks you to combine them in a type-safe quiz engine.
Checkpoint
Answer all three to mark this lesson complete