How do you write a user-defined type guard with the is keyword?
Declare a function whose return type is a type predicate, like (x: unknown): x is User. When it returns true, TypeScript treats x as User in that branch. The compiler trusts your logic, so the check inside must be correct.
Explain it simply
Built-in checks like typeof cover simple cases. For your own shapes, such as data arriving from an API, you write the check yourself and tell TypeScript what a true result means.
The return type x is User is a promise: "if I say true, this is a User". TypeScript does not verify the promise. If your function returns true for the wrong things, you get runtime bugs with no compiler warning.
Example
interface User { name: string; age: number }
function isUser(x: unknown): x is User {
return (
typeof x === "object" &&
x !== null &&
typeof (x as User).name === "string" &&
typeof (x as User).age === "number"
);
}
function greet(data: unknown) {
if (isUser(data)) {
console.log(data.name.toUpperCase()); // data: User here
} else {
console.log("not a user");
}
}
greet({ name: "Asha", age: 21 });What to say in the interview
"A type predicate, x is User, lets me package a runtime check as a reusable guard. After it returns true, TypeScript narrows to User. It is only as safe as the check inside, so for large payloads I would use a schema library such as Zod instead of hand-written guards."
Common mistakes
- Writing a guard that checks only one field and returns true for incomplete objects.