Narrowing and Type Guards Interview Questions
What is narrowing? Which constructs narrow types?
Narrowing is how TypeScript refines a broad type to a more specific one inside a code branch, using control flow analysis. Constructs: typeof, instanceof, in, equality (===, == null), truthiness, Array.isArray, discriminant property checks, user-defined type guards (x is T) and assertion functions (asserts x is T).
What is a discriminated union and how do you make a switch exhaustive?
A union of object types that share a literal property (the discriminant). Checking it narrows to one member. For exhaustiveness, assign the value to never in the default branch: adding a new member later produces a compile error there.
Exhaustiveness Check
type Payment = { kind: "upi"; vpa: string } | { kind: "card"; last4: string } | { kind: "wallet"; provider: string };
function label(p: Payment): string {
switch (p.kind) {
case "upi": return "UPI " + p.vpa;
case "card": return "Card **" + p.last4;
default: {
const unhandled: never = p; // error: "wallet" is not handled
return unhandled;
}
}
}Compiler output (tsc)
Line 8: error TS2322: Type '{ kind: "wallet"; provider: string; }' is not assignable to type 'never'.What is a user-defined type guard? How is it different from an assertion function?
A type guard returns boolean with the return type value is T; the caller narrows inside an if. An assertion function returns void with asserts value is T and throws on failure; code after the call is narrowed. Both are only as correct as their implementation: TypeScript trusts them.
Guard vs Assertion
type User = { id: number; email: string };
const isUser = (x: unknown): x is User =>
typeof x === "object" && x !== null && "id" in x && typeof x.id === "number" && "email" in x && typeof x.email === "string";
function assertUser(x: unknown): asserts x is User {
if (!isUser(x)) throw new TypeError("Not a user");
}
const data: unknown = JSON.parse('{"id": 1, "email": "[email protected]"}');
if (isUser(data)) console.log("guard:", data.email);
assertUser(data);
console.log("assert:", data.id);Output
guard: [email protected] assert: 1
Why can typeof x === "object" not remove null?
Because in JavaScript typeof null === "object". TypeScript models this, so after that check the type is still object | null; add x !== null. Also remember truthiness checks remove 0, "" and NaN, which can be a bug for numbers; prefer x != null or ??.