instanceof & typeof
Type Checking Done Right
LinkedIn Hook
typeof null === "object".That's a 30-year-old bug shipped in the very first version of JavaScript in 1995. A fix was proposed for ES6 and REJECTED because too much production code depends on the broken behavior.
And that's just the start of JavaScript's type-checking weirdness:
typeof []is"object"(arrays are objects)"hello" instanceof Stringisfalse(primitives aren't instances)- An array from an iframe is NOT
instanceof Arrayin the parent windowSo how do you actually check types safely? You combine three tools:
typeoffor primitives,instanceoffor walking the prototype chain, andObject.prototype.toString.call(value)for the one reliable answer that works everywhere.In this lesson you'll learn exactly how
instanceofwalks the chain, whytypeof nullwill never be fixed, how to write a polyfill forinstanceof, and howSymbol.hasInstancelets you customize what "being an instance" even means.Read the full lesson -> [link]
#JavaScript #InterviewPrep #TypeChecking #Prototypes #Frontend #CodingInterview #WebDevelopment
What You'll Learn
- Why
typeof null === "object"and why that bug will never be fixed - How
instanceofwalks the prototype chain to determine type, and how to polyfill it - Reliable type-check patterns using
Array.isArray,Symbol.hasInstance, andObject.prototype.toString.call
typeof — Simple but Quirky
typeof returns a string indicating the type of the operand. It's straightforward for most values, but has some famous quirks.
// The normal cases:
console.log(typeof "hello"); // "string"
console.log(typeof 42); // "number"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof Symbol()); // "symbol"
console.log(typeof BigInt(42)); // "bigint"
console.log(typeof function(){}); // "function"
console.log(typeof {}); // "object"
console.log(typeof []); // "object" (arrays are objects)
// THE FAMOUS BUG:
console.log(typeof null); // "object" <- THIS IS A BUG!
Why typeof null === "object" — The 30-Year-Old Bug
In the very first implementation of JavaScript (1995), values were stored as a type tag + value. Objects had type tag 0. null was represented as the NULL pointer (0x00). So when typeof checked the type tag of null, it saw 0 and returned "object".
This was a bug in the original engine. A fix was proposed for ES6 (typeof null === "null"), but it was rejected because too much existing code depended on the buggy behavior. It will never be fixed.
// Safe null check (typeof won't work!):
function isObject(val) {
return val !== null && typeof val === "object";
}
console.log(isObject({})); // true
console.log(isObject(null)); // false (correctly excluded)
console.log(isObject([])); // true (arrays are objects)
instanceof — Walking the Prototype Chain
instanceof checks whether an object has a specific constructor's .prototype anywhere in its prototype chain.
function Animal(name) {
this.name = name;
}
function Dog(name) {
Animal.call(this, name);
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;
const rex = new Dog("Rex");
// instanceof walks the chain:
console.log(rex instanceof Dog); // true (Dog.prototype is in rex's chain)
console.log(rex instanceof Animal); // true (Animal.prototype is in rex's chain)
console.log(rex instanceof Object); // true (Object.prototype is in rex's chain)
// How it works internally:
// rex.__proto__ === Dog.prototype? YES -> true
// rex.__proto__.__proto__ === Animal.prototype? YES -> true
// rex.__proto__.__proto__.__proto__ === Object.prototype? YES -> true
How instanceof Actually Works
// instanceof essentially does this:
function myInstanceof(obj, Constructor) {
let proto = Object.getPrototypeOf(obj);
while (proto !== null) {
if (proto === Constructor.prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
// Test it:
console.log(myInstanceof(rex, Dog)); // true
console.log(myInstanceof(rex, Animal)); // true
console.log(myInstanceof(rex, Array)); // false
console.log(myInstanceof([], Array)); // true
Polyfill for instanceof
A complete, interview-ready polyfill:
function polyfillInstanceof(obj, Constructor) {
// Handle edge cases
if (obj === null || obj === undefined) return false;
if (typeof obj !== "object" && typeof obj !== "function") return false;
if (typeof Constructor !== "function") {
throw new TypeError("Right-hand side of instanceof is not callable");
}
// Check for Symbol.hasInstance first (ES6+)
if (typeof Constructor[Symbol.hasInstance] === "function") {
return Constructor[Symbol.hasInstance](obj);
}
// Walk the prototype chain
let proto = Object.getPrototypeOf(obj);
while (proto !== null) {
if (proto === Constructor.prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}
Symbol.hasInstance — Custom instanceof Behavior
ES6 lets you override what instanceof does with Symbol.hasInstance:
class EvenNumber {
static [Symbol.hasInstance](instance) {
return typeof instance === "number" && instance % 2 === 0;
}
}
console.log(2 instanceof EvenNumber); // true
console.log(3 instanceof EvenNumber); // false
console.log(4 instanceof EvenNumber); // true
console.log("4" instanceof EvenNumber); // false (not a number)
// Practical example: type-checking validator
class Validator {
static [Symbol.hasInstance](instance) {
return instance !== null && instance !== undefined && typeof instance.validate === "function";
}
}
const form = {
validate() { return true; }
};
console.log(form instanceof Validator); // true (has validate method)
console.log({} instanceof Validator); // false
instanceof Edge Cases and Gotchas
// 1. Primitives always return false
console.log("hello" instanceof String); // false (it's a primitive, not an object)
console.log(42 instanceof Number); // false
console.log(true instanceof Boolean); // false
// 2. Wrapper objects DO work
console.log(new String("hello") instanceof String); // true
console.log(new Number(42) instanceof Number); // true
// 3. Arrays
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true (Array inherits from Object)
// 4. Cross-frame/realm issues
// If you create an array in an iframe, it won't be instanceof Array
// in the parent window (different Array constructors!)
// Solution: Use Array.isArray()
console.log(Array.isArray([])); // true (works across realms)
Practical Type Checking Patterns
// The robust type checker:
function getType(value) {
if (value === null) return "null";
if (value === undefined) return "undefined";
if (Array.isArray(value)) return "array";
if (value instanceof RegExp) return "regexp";
if (value instanceof Date) return "date";
if (value instanceof Error) return "error";
if (value instanceof Promise) return "promise";
return typeof value; // "string", "number", "boolean", "function", "object", etc.
}
console.log(getType(null)); // "null"
console.log(getType([])); // "array"
console.log(getType({})); // "object"
console.log(getType(new Date())); // "date"
console.log(getType(/regex/)); // "regexp"
console.log(getType(Promise.resolve())); // "promise"
// The Object.prototype.toString trick (most reliable):
function preciseType(value) {
return Object.prototype.toString.call(value).slice(8, -1).toLowerCase();
}
console.log(preciseType(null)); // "null"
console.log(preciseType([])); // "array"
console.log(preciseType({})); // "object"
console.log(preciseType(new Date())); // "date"
console.log(preciseType(/regex/)); // "regexp"
console.log(preciseType(42)); // "number"
Common Mistakes
- Using
typeof val === "object"to check for objects without excludingnull— you'll treatnullas an object. Always guard withval !== null. - Using
arr instanceof Arrayin code that may receive arrays from iframes, workers, or other realms — different realms have differentArrayconstructors. UseArray.isArray(arr)instead. - Expecting
"hello" instanceof Stringto returntrue— primitives are never instances. Only boxed objects likenew String("hello")are.
Interview Questions
Q: Why does typeof null return "object"?
It's a bug from the original JavaScript implementation. Values were stored with type tags, and null was represented as a NULL pointer (0x00). Since objects had type tag 0,
typeofmistakenly identified null as an object. This bug was never fixed because too much code depends on it.
Q: How does instanceof work internally?
instanceofchecks if the constructor's.prototypeexists anywhere in the object's prototype chain. It walks up the chain via__proto__links, comparing each prototype withConstructor.prototype. Returnstrueif found,falseif it reachesnull.
Q: Why doesn't "hello" instanceof String return true?
Because
"hello"is a primitive, not an object.instanceofonly works on objects. The primitive string is auto-boxed temporarily for method calls, but it's not actually an instance ofString. Usetypeoffor primitive checks andinstanceoffor object checks.
Q: What is Symbol.hasInstance?
It's a well-known Symbol that lets you customize the behavior of
instanceof. By defining a static[Symbol.hasInstance]method on a class, you can control whatinstanceofreturns for that class.
Quick Reference — Cheat Sheet
TYPE CHECKING — QUICK MAP
typeof
- checks the internal type tag
- returns "string" | "number" | "boolean" | "undefined"
| "symbol" | "bigint" | "function" | "object"
- typeof null === "object" (BUG, never fixed)
- typeof [] === "object" (arrays are objects)
instanceof
- walks the __proto__ chain
- obj instanceof Fn
-> is Fn.prototype anywhere in obj's chain?
- primitives always return false
- breaks across realms/iframes (different constructors)
Reliable patterns
Array.isArray(val) // cross-realm safe
Object.prototype.toString.call(val) // "[object Date]" etc.
val !== null && typeof val === "object" // true object check
Customize instanceof
class C { static [Symbol.hasInstance](v){ ... } }
Previous: ES6 Classes (Syntactic Sugar) Next: First-Class Functions
This is Lesson 5.5 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.