mediumFull Stack EngineerProduct
Explain TypeScript type narrowing and the different ways to narrow types at runtime
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a product company interview:
> "We have a function that handles different response types from our API. Each response has a different shape based on the
> "We have a function that handles different response types from our API. Each response has a different shape based on the
type field. How do we safely handle this in TypeScript?"Suggested Solution
Type Narrowing Techniques
1. typeof (primitives)
function process(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase(); // TypeScript knows it's string
}
return value.toFixed(2); // TypeScript knows it's number
}
2. instanceof (classes)
function handleError(error: Error | ValidationError | AuthError) {
if (error instanceof ValidationError) {
return { status: 400, message: error.field }; // Access .field
}
if (error instanceof AuthError) {
return { status: 401, message: error.action }; // Access .action
}
return { status: 500, message: error.message };
}
3. Discriminated Unions (BEST for API responses)
type ApiResponse =
| { type: "success"; data: User[] }
| { type: "error"; error: string; code: number }
| { type: "loading" };
function handleResponse(res: ApiResponse) {
switch (res.type) {
case "success":
return res.data; // TypeScript knows data exists
case "error":
return res.code; // TypeScript knows code exists
case "loading":
return "Loading...";
}
}
4. Type Predicates
function isUser(value: unknown): value is User {
return typeof value === "object" && value !== null && "email" in value;
}
if (isUser(data)) {
console.log(data.email); // Safe!
}
5. Assertion Functions
function assertDefined<T>(value: T | undefined): asserts value is T {
if (value === undefined) throw new Error("Expected defined value");
}
const user = users.find(u => u.id === id);
assertDefined(user);
console.log(user.name); // TypeScript knows user is defined