Type Guards in TypeScript
Type Guards in TypeScript
Section titled “Type Guards in TypeScript”What are Type Guards?
Section titled “What are Type Guards?”Type guards are expressions that perform runtime checks to narrow a type within a scope. They tell TypeScript’s compiler: “in this block, this value is definitely this specific type.”
Type Narrowing Flow
Section titled “Type Narrowing Flow”flowchart TB Wide[Wide Type<br/>string | number | boolean] --> Check{Type Guard Check} Check -->|typeof === 'string' ✅| Narrow1[Narrowed: string<br/>Can use .toUpperCase()] Check -->|typeof === 'number' ✅| Narrow2[Narrowed: number<br/>Can use .toFixed()] Check -->|typeof === 'boolean' ✅| Narrow3[Narrowed: boolean<br/>Can use ! negation]
style Wide fill:#7c3aed,color:#fff style Check fill:#f59e0b,color:#fff style Narrow1 fill:#3b82f6,color:#fff style Narrow2 fill:#059669,color:#fff style Narrow3 fill:#ec4899,color:#fffAnalogy: A wide type is like a box labeled “mixed items.” A type guard is like opening the box and checking each item — once you know it’s a hammer, you can use it as a hammer.
Built-in Type Guards
Section titled “Built-in Type Guards”// typeof — for primitivesfunction process(value: string | number) { if (typeof value === "string") { return value.toUpperCase(); // value is string } return value.toFixed(2); // value is number}
// instanceof — for classesclass Dog { bark() {} }class Cat { meow() {} }
function makeSound(pet: Dog | Cat) { if (pet instanceof Dog) { pet.bark(); // pet is Dog } else { pet.meow(); // pet is Cat }}
// in — for property checkinginterface Bird { fly(): void }interface Fish { swim(): void }
function move(animal: Bird | Fish) { if ("fly" in animal) { animal.fly(); // animal is Bird } else { animal.swim(); // animal is Fish }}User-Defined Type Guards
Section titled “User-Defined Type Guards”// Custom type guard using "is"interface User { name: string; email: string; role: "user" }interface Admin { name: string; email: string; role: "admin"; permissions: string[] }
function isAdmin(user: User | Admin): user is Admin { return user.role === "admin";}
function getUserDashboard(user: User | Admin) { if (isAdmin(user)) { return user.permissions; // user is Admin } return ["read"]; // user is User}Real Project Example
Section titled “Real Project Example”// API response type guardtype ApiResult<T> = | { success: true; data: T } | { success: false; error: string };
function isSuccess<T>(result: ApiResult<T>): result is { success: true; data: T } { return result.success;}
// Usageconst result = await fetchUser();if (isSuccess(result)) { console.log(result.data.name); // Safe access} else { console.error(result.error); // Error access}Interview Questions
Section titled “Interview Questions”Easy: What is a type guard? Give an example with typeof.
Medium: How do you create a user-defined type guard?
Hard: When would you use the in operator as a type guard vs a custom type guard function?