typeof & instanceof
Wide type goes in, narrow type comes out after the check.
Type narrowing is TypeScript's superpower of taking a WIDE type and making it NARROWER based on a check you perform. The two most basic checks are typeof (for primitives) and instanceof (for classes). Without narrowing, TypeScript only knows the broad union type, and it won't let you access specific properties or methods because it can't guarantee they exist. Narrowing is how you prove to TypeScript that a value is a more specific type.
Think of the RTA Checkpost on the Outer Ring Road. Every vehicle approaches as just "Vehicle" — could be a truck, car, bike, auto, or bus. The checkpost inspector does a quick check: typeof is like looking at the vehicle CATEGORY board — "2-wheeler or 4-wheeler?" Based on the board, the inspector routes you to the correct lane. instanceof is like checking the actual REGISTRATION CERTIFICATE — "Is this vehicle specifically a Tata Ace truck?" The RC gives you the EXACT class, not just the category.
After the check, the inspector KNOWS exactly what type of vehicle is in each lane. TypeScript does the same — after a typeof or instanceof check, it KNOWS the narrowed type and gives you correct autocomplete and type safety. No more "this might be anything" — the checkpost narrows it down! This is what makes TypeScript feel smart: it understands your runtime checks and uses them to refine the types at compile time.
The typeof operator returns a string describing the runtime type. TypeScript uses it to NARROW the type in each branch of your conditional logic. It's the simplest and most common form of type narrowing, perfect for distinguishing between primitive types.
typeof Return Values:
typeof "hello" // "string"
typeof 42 // "number"
typeof true // "boolean"
typeof undefined // "undefined"
typeof BigInt(1) // "bigint"
typeof Symbol() // "symbol"
typeof {} // "object"
typeof function(){} // "function"
Narrowing in Action:
function pad(
value: string | number
): string {
if (typeof value === "string") {
// TS knows: value is string
return value;
}
// TS knows: value is number
return String(value);
}
Inside the if block, TypeScript knows value is string and gives you string methods like .toUpperCase(). Outside the if (in the else branch), TypeScript knows value must be number because the string case was already handled.
Multiple Branches:
function process(
x: string | number | boolean
) {
if (typeof x === "string") {
// x is string
} else if (typeof x === "number") {
// x is number
} else {
// x is boolean
}
}
typeof works best for primitives. For objects, typeof always returns "object" — even arrays and null! typeof null === "object" is a JavaScript bug from 1995 that will NEVER be fixed. It's a permanent part of the language, and you just have to remember it.
The instanceof operator checks if an object is an instance of a specific class (or any class in its prototype chain). TypeScript uses it to narrow to that class type. Unlike typeof which only gives you a broad category, instanceof tells you the EXACT class the object was created from.
Basic Example:
function logValue(
x: Date | string
) {
if (x instanceof Date) {
// TS knows: x is Date
console.log(x.toUTCString());
} else {
// TS knows: x is string
console.log(x.toUpperCase());
}
}
Custom Classes:
class Auto {
fare() { return 30; }
}
class Metro {
fare() { return 60; }
}
function getFare(
transport: Auto | Metro
): number {
if (transport instanceof Auto) {
// TS knows: transport is Auto
return transport.fare();
}
// TS knows: transport is Metro
return transport.fare();
}
Prototype Chain: instanceof checks the ENTIRE prototype chain. new Array() instanceof Object is true because Array extends Object. This means instanceof can match parent classes too, not just the exact class.
Limitation: instanceof only works with CLASS instances, not plain objects. You can't use instanceof to check plain object shapes.
const obj = { name: "hello" };
obj instanceof Object; // true
obj instanceof String; // false
// Can't check object shapes!
For checking plain object shapes, use the in operator or custom type guards — we'll cover those in chapters 6.3 and 6.5. Think of it this way: instanceof checks the RC (Registration Certificate) for class-based vehicles, but it can't identify a plain object just by looking at its shape.
typeof and instanceof are essential tools, but they come with traps that have confused developers for decades. Knowing these gotchas will save you hours of debugging and prevent subtle bugs in your code.
Trap 1: typeof null is "object"
typeof null === "object" // true!
// CORRECT way to check for null:
if (x === null) {
// Now TS knows x is null
}
This is the #1 typeof trap. It's a bug from JavaScript's first implementation in 1995 that can never be fixed because too much web code relies on it. Always use strict equality === null to check for null.
Trap 2: typeof array is "object"
typeof [] === "object" // true!
// CORRECT way to check for arrays:
if (Array.isArray(x)) {
// TS knows x is an array
}
Arrays are objects in JavaScript, so typeof can't distinguish them. Always use Array.isArray() — it's the only reliable way.
Trap 3: typeof doesn't distinguish object shapes
typeof { a: 1 } // "object"
typeof { b: "hi" } // "object"
// Both return "object"!
typeof cannot tell you what properties an object has. It just says "this is an object." For shape checks, use the in operator.
Trap 4: instanceof needs a class, not a type
// WRONG! Right side must be a class
x instanceof { name: string }
// RIGHT! Right side is a class
x instanceof String
Trap 5: instanceof across realms — an object created in an iframe or Node.js vm module won't be instanceof the parent realm's Array. The prototype chains are different because each realm has its own global objects.
Trap 6: typeof in switch — switch(typeof x) works for narrowing, but you MUST have explicit cases, not fall-through, for TypeScript to properly narrow the type in each case block.
Here's your complete cheatsheet for typeof and instanceof narrowing. Pin this to your mental whiteboard — it captures everything you need to remember about these two fundamental narrowing tools.
typeof — Primitive Category Check:
if (typeof x === "string") {
// x is string
}
if (typeof x === "number") {
// x is number
}
if (typeof x === "boolean") {
// x is boolean
}
instanceof — Class Identity Check:
if (x instanceof Date) {
// x is Date
}
if (x instanceof Error) {
// x is Error
}
if (x instanceof Auto) {
// x is Auto
}
typeof Returns: "string", "number", "boolean", "undefined", "object", "function", "bigint", "symbol".
typeof GOTCHAS:
null→ "object" (use=== null)- array → "object" (use
Array.isArray()) - plain object → "object" (use
inoperator)
Key Rules:
- typeof for primitives — quick category check
- instanceof for classes — registration certificate check
- Both narrow the type in the true branch
- typeof can't distinguish object shapes
- instanceof doesn't work across realms (iframes, vm)
The Golden Rule: typeof and instanceof are the RTA checkpost's first glance — quick category check for primitives, registration check for classes. But for deep inspection, you need more advanced tools, bhai!
Key Points
- Type narrowing takes a wide type and makes it narrower based on a runtime check
- typeof checks primitive categories — returns a string like "string" or "number"
- instanceof checks class identity — verifies the exact class or prototype chain
- typeof null is "object" and typeof array is "object" — use === null and Array.isArray()
- instanceof only works with class instances, not plain object shapes
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login