typeof: Why It Lies
typeof null === 'object' — yeh machine kabhi sach nahi bolta bhai.
typeof returns a STRING — not a type, not a boolean. It is a unary operator that inspects a value and tells you what JavaScript "thinks" the type is. There are only 9 possible return values: 'undefined', 'boolean', 'number', 'bigint', 'string', 'symbol', 'object', 'function' — and 'object' for null (a bug).
Note: 'function' is NOT a JS type — functions are objects. typeof special-cases them because they are callable. And typeof null === 'object' is a permanent bug from 1995.
typeof is safe to call even on UNDECLARED variables — no ReferenceError! This is the ONE case where typeof is irreplaceable.
But watch out: typeof with let/const in TDZ does throw ReferenceError (unlike undeclared).
// The complete typeof truth table:
console.log(typeof undefined); // 'undefined'
console.log(typeof true); // 'boolean'
console.log(typeof 42); // 'number'
console.log(typeof 42n); // 'bigint'
console.log(typeof 'hello'); // 'string'
console.log(typeof Symbol()); // 'symbol'
console.log(typeof {}); // 'object'
console.log(typeof []); // 'object' — array IS an object
console.log(typeof null); // 'object' — THE LIE
console.log(typeof function(){}); // 'function' — special case!
console.log(typeof (() => {})); // 'function' — arrow too
console.log(typeof class Foo{}); // 'function' — class IS a function
// The ONE valid use: safe undeclared variable check
// console.log(undeclaredVar); // ReferenceError!
console.log(typeof undeclaredVar); // 'undefined' — NO error!
// TDZ exception — let/const behave differently:
// console.log(typeof tdzVar); // ReferenceError! (not 'undefined')
// let tdzVar = 1;
if (typeof localStorage !== 'undefined') — if localStorage did not exist at all, this was the only safe check. In module-based code this matters less, but it is still the only safe guard against undeclared variables.typeof lies — and not just about null. It is technically correct for arrays, dates, and regex (they ARE objects), but completely unhelpful when you need to know the specific kind of object.
typeof null === 'object' — null is a primitive, not an object. This is a permanent 1995 bug.
typeof [] === 'object' — array is an object (technically correct but useless for distinguishing arrays from objects).
typeof new Date() === 'object' — Date instance (correct but unhelpful).
typeof /regex/ === 'object' — RegExp is an object (correct but unhelpful).
typeof NaN === 'number' — NaN IS the number type (surprising but correct per spec).
For all these: better alternatives exist.
// typeof lies and better alternatives:
// null — typeof lies
console.log(typeof null); // 'object' — LIE
console.log(null === null); // true — CORRECT check
console.log(null == undefined); // true — catches both
// Array — typeof is useless
console.log(typeof []); // 'object' — useless
console.log(Array.isArray([])); // true — correct
console.log([] instanceof Array); // true
// Date — typeof useless
console.log(typeof new Date()); // 'object' — useless
console.log(new Date() instanceof Date); // true
// RegExp — typeof useless
console.log(typeof /abc/); // 'object' — useless
console.log(/abc/ instanceof RegExp); // true
// NaN — typeof technically correct but confusing
console.log(typeof NaN); // 'number' — correct but surprising
console.log(Number.isNaN(NaN)); // true — use this
// The REAL type checking: Object.prototype.toString
function getType(value) {
return Object.prototype.toString.call(value);
}
console.log(getType(null)); // '[object Null]'
console.log(getType([])); // '[object Array]'
console.log(getType(new Date())); // '[object Date]'
console.log(getType(/abc/)); // '[object RegExp]'
console.log(getType(42)); // '[object Number]'
console.log(getType(NaN)); // '[object Number]'
instanceof checks the PROTOTYPE CHAIN — not the type itself. It walks up the __proto__ chain looking for Constructor.prototype.
It works correctly for custom classes and built-ins in the same realm. But it has two serious limitations:
1. Fails across iframes/realms: An Array from an iframe is NOT instanceof Array in the parent — different prototype objects.
2. Can be fooled: Object.create(SomeClass.prototype) passes instanceof without ever calling the constructor.
You can also override instanceof behavior with Symbol.hasInstance.
// instanceof walks the prototype chain
class Animal {}
class Dog extends Animal {}
const d = new Dog();
console.log(d instanceof Dog); // true — Dog.prototype in chain
console.log(d instanceof Animal); // true — Animal.prototype in chain
console.log(d instanceof Object); // true — Object.prototype in chain
// instanceof with built-ins — works in same realm
console.log([] instanceof Array); // true
console.log([] instanceof Object); // true — arrays are objects
console.log({} instanceof Array); // false
// Cross-realm failure:
// const iframe = document.createElement('iframe');
// document.body.appendChild(iframe);
// const iframeArray = iframe.contentWindow.Array;
// const arr = new iframeArray();
// console.log(arr instanceof Array); // false! different Array constructor
// console.log(Array.isArray(arr)); // true — realm-safe check
// instanceof can be fooled:
const fakeArray = Object.create(Array.prototype);
console.log(fakeArray instanceof Array); // true — but it is not an Array!
console.log(Array.isArray(fakeArray)); // false — correct
// Custom Symbol.hasInstance:
class EvenNumber {
static [Symbol.hasInstance](num) {
return typeof num === 'number' && num % 2 === 0;
}
}
console.log(2 instanceof EvenNumber); // true!
console.log(3 instanceof EvenNumber); // false!
console.log(4 instanceof EvenNumber); // true!
Stop guessing — here is the complete right-tool guide for every type check you will ever need:
// The complete right-tool guide:
function typeChecks(x) {
console.log('--- Checking:', x, '---');
// Primitives
if (x === undefined) console.log('undefined');
if (x === null) console.log('null');
if (x == null) console.log('null OR undefined');
if (typeof x === 'boolean') console.log('boolean:', x);
if (typeof x === 'number' && !Number.isNaN(x)) console.log('valid number:', x);
if (Number.isNaN(x)) console.log('NaN');
if (Number.isFinite(x)) console.log('finite number');
if (Number.isInteger(x)) console.log('integer');
if (typeof x === 'string') console.log('string:', x);
if (typeof x === 'bigint') console.log('bigint:', x);
if (typeof x === 'symbol') console.log('symbol');
// Objects
if (Array.isArray(x)) console.log('array, length:', x.length);
if (typeof x === 'function') console.log('function:', x.name);
if (x instanceof Date) console.log('Date:', x.toISOString());
if (x instanceof RegExp) console.log('RegExp:', x.source);
// Generic object (not array, not null)
if (typeof x === 'object' && x !== null && !Array.isArray(x)) {
console.log('plain object or class instance');
}
// Nuclear option — always accurate
console.log('toString:', Object.prototype.toString.call(x));
}
typeChecks(null); // null, null OR undefined
typeChecks(undefined); // undefined, null OR undefined
typeChecks(42); // valid number, finite, integer
typeChecks(3.14); // valid number, finite (not integer)
typeChecks([1,2,3]); // array
typeChecks(() => {}); // function
x === undefined or typeof x === 'undefined' (safe for undeclared). null → x === null. null OR undefined → x == null. NaN → Number.isNaN(x). finite number → Number.isFinite(x). integer → Number.isInteger(x). array → Array.isArray(x). function → typeof x === 'function'. class instance → x instanceof MyClass. exact built-in → Object.prototype.toString.call(x).typeof is NOT a function call — it is a unary operator. V8 compiles it into a single type tag check on the 64-bit value representation.
For tagged pointers: check the low bits — 0 = SMI (small integer, a number), 1 = heap object pointer.
For heap objects: read the Map (hidden class) to get the instance type — that determines what string typeof returns.
Special case for functions: the Map has a 'callable' flag → typeof returns 'function'.
Special case for null: null has all-zero bits including the heap pointer tag → V8 reads it as a heap object → returns 'object'.
typeof on undeclared: the bytecode does a special scope chain lookup that returns undefined instead of throwing.
// typeof under the hood (conceptual pseudocode):
// function typeofOperator(value) {
// if (isSMI(value)) return 'number'; // low bit = 0 → SMI
//
// const heapObj = value.deref(); // dereference the pointer
// const map = heapObj.map; // read the hidden class
//
// switch(map.instanceType) {
// case UNDEFINED_TYPE: return 'undefined';
// case NULL_TYPE: return 'object'; // the bug: null bits
// case BOOLEAN_TYPE: return 'boolean';
// case HEAP_NUMBER: return 'number';
// case BIGINT_TYPE: return 'bigint';
// case STRING_TYPE: return 'string';
// case SYMBOL_TYPE: return 'symbol';
// case JS_FUNCTION: return 'function'; // callable check
// case JS_OBJECT: return 'object';
// // ... more cases
// }
// }
// Why typeof undeclared is safe:
// V8 bytecode for typeof: LdaLookupContextSlot 'varName'
// This lookup returns undefined for missing variables
// instead of throwing — special typeof-only behavior in V8
// But TDZ let/const: the slot EXISTS but is marked "uninitialized"
// LdaLookupContextSlot finds the slot, sees "uninitialized" → throws ReferenceError
// typeof cannot save you from TDZ!
Lo kar liya — Key Points:
- ✅ typeof returns one of 8 strings — 'function' is special-cased (functions are objects), 'object' covers null (bug), arrays, dates, regex
- ✅
typeof null === 'object'is a permanent 1995 bug — always check null with=== nullexplicitly - ✅ typeof is the ONLY safe check for undeclared variables — but throws ReferenceError for TDZ let/const
- ✅
instanceofwalks the prototype chain — fails across iframes. UseArray.isArray()for realm-safe array checks - ✅
Object.prototype.toString.call(x)is the most accurate type check — reveals '[object Array]', '[object Date]', etc. - ✅ Right tool for each:
=== nullfor null, typeof for primitives,Array.isArray()for arrays, instanceof for classes
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