typeof Guards
typeof Guards
The typeof operator checks the type of a value at runtime, allowing TypeScript to narrow the type.
Basic typeof
function process(value: string | number | boolean) {
if (typeof value === "string") {
// TypeScript knows value is string
return value.toUpperCase();
} else if (typeof value === "number") {
// TypeScript knows value is number
return value.toFixed(2);
} else {
// TypeScript knows value is boolean
return value ? "yes" : "no";
}
}
typeof with undefined
function greet(name: string | undefined) {
if (typeof name === "string") {
return `Hello, ${name}`;
}
return "Hello, stranger";
}
typeof in Callbacks
function handleEvent(event: KeyboardEvent | MouseEvent) {
if (typeof event.key === "string") {
// KeyboardEvent
console.log(event.key);
} else {
// MouseEvent
console.log(event.clientX);
}
}
Limitations
typeof only works with primitives:
function process(value: string | object) {
if (typeof value === "object") {
// Can't distinguish between different object types
// value is still string | object
}
}
instanceof Guards
instanceof Guards
The instanceof operator checks if an object is an instance of a specific class.
Basic instanceof
class HttpError {
constructor(public statusCode: number, public message: string) {}
}
class NetworkError {
constructor(public message: string) {}
}
function handleError(error: HttpError | NetworkError) {
if (error instanceof HttpError) {
console.log(`HTTP ${error.statusCode}: ${error.message}`);
} else {
console.log(`Network: ${error.message}`);
}
}
instanceof with Built-in Classes
function processDate(value: Date | string) {
if (value instanceof Date) {
return value.getFullYear();
}
return new Date(value).getFullYear();
}
instanceof with Error Types
function catchBlock(error: unknown) {
if (error instanceof TypeError) {
console.log("Type error:", error.message);
} else if (error instanceof RangeError) {
console.log("Range error:", error.message);
} else if (error instanceof Error) {
console.log("Generic error:", error.message);
}
}
instanceof vs typeof
// Use typeof for primitives
function isString(value: unknown): value is string {
return typeof value === "string";
}
// Use instanceof for classes
function isError(value: unknown): value is Error {
return value instanceof Error;
}
Discriminated Unions
Discriminated Unions
Discriminated unions use a common property to narrow types in switch statements.
Pattern
type Shape =
| { kind: "circle"; radius: number }
| { kind: "rectangle"; width: number; height: number }
| { kind: "triangle"; base: number; height: number };
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
// TypeScript knows shape is circle
return Math.PI * shape.radius ** 2;
case "rectangle":
// TypeScript knows shape is rectangle
return shape.width * shape.height;
case "triangle":
// TypeScript knows shape is triangle
return (shape.base * shape.height) / 2;
}
}
Exhaustive Switch
function area(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
case "triangle":
return (shape.base * shape.height) / 2;
default:
const _exhaustive: never = shape;
return _exhaustive;
}
}
Real-World Example
type Action =
| { type: "INCREMENT"; amount: number }
| { type: "DECREMENT"; amount: number }
| { type: "RESET" };
function reducer(state: number, action: Action): number {
switch (action.type) {
case "INCREMENT":
return state + action.amount;
case "DECREMENT":
return state - action.amount;
case "RESET":
return 0;
}
}
Quiz
1. What does typeof check?
2. When should you use instanceof?
3. What is a discriminated union?
4. What does the never type do in a switch?
Flashcards
Question
What does typeof guard check?
Click to reveal answer
Answer
Primitive types: string, number, boolean, undefined, etc.
Question
When do you use instanceof?
Click to reveal answer
Answer
To check if an object is an instance of a class
Question
What makes a discriminated union?
Click to reveal answer
Answer
A common literal property that distinguishes between types
Question
How do you ensure exhaustive type checking?
Click to reveal answer
Answer
Use the never type in a default switch case
Revision Notes
Key Takeaways
- 1. typeof guards narrow primitive types
- 2. instanceof guards narrow class instances
- 3. Discriminated unions use a common property for narrowing
- 4. Exhaustive checking ensures all cases are handled
- 5. Type narrowing happens automatically in control flow
Interview Tips
- • Explain when to use typeof vs instanceof
- • Show how discriminated unions simplify complex logic
- • Demonstrate exhaustive checking with never
Cheat Sheet
Cheat Sheet
typeof
if (typeof x === "string") {
// x is string
}
instanceof
if (x instanceof Error) {
// x is Error
}
Discriminated Union
type Shape =
| { kind: "circle"; r: number }
| { kind: "rect"; w: number; h: number };
switch (shape.kind) {
case "circle": //...
case "rect": //...
}
Exhaustive Check
default:
const _: never = shape;
return _;