In JavaScript, what does the typeof operator return for null, for an array, for a function or class, and for a variable that was never declared?
answer
- one operator, a fixed set of strings
- arrays and dates look identical
- callables get their own result
- safe on a name that never existed
- null shares the object tag
basics
~20 stypeof null returns "object" — a historic bug kept for compatibility. Arrays return "object" too, so typeof cannot spot them. Functions and classes return "function". typeof on a never-declared name safely returns "undefined" instead of throwing.
solid answer
~50 s`typeof` returns a string naming a value's type, and the corners matter more than the happy path. `typeof null` is `"object"`, a bug inherited from the very first implementation where the null pointer carried the same internal tag as objects; it was never fixed because too much code depends on it, so a null test needs `value === null`, or `value !== null && typeof value === "object"` for "a real object". Arrays, dates, regexes and plain objects all report `"object"`, so `typeof` cannot tell them apart. Anything callable — function declarations, arrow functions, and `class` declarations — reports `"function"`. And `typeof` is the only operator that tolerates a completely undeclared name: `typeof missing` evaluates to `"undefined"` rather than throwing a ReferenceError, which is why old feature-detection code was written that way. The one exception is a `let` or `const` binding used before its declaration, which still throws.
code
javascript · 10 linesconsole.log(typeof neverDeclaredAnywhere); // "undefined" — no ReferenceError
function isObject(value) {
return value !== null && typeof value === "object";
}
console.log(isObject(null)); // false
console.log(isObject([])); // true
console.log(isObject(new Date())); // true
console.log(typeof class Foo {}); // "function"go deeper
Memorise the odd ones: null gives "object", arrays give "object", functions and classes give "function". Say plainly that a null test needs === null, and that typeof cb === "function" is the right guard before calling a callback.
Explain the mechanism, not just the table: the historic type-tag collision behind null, the internal call method behind "function", and the specification's special case that lets typeof survive an unresolvable identifier while a temporal-dead-zone binding still throws.
Show where a typeof-based guard silently lets bad input through in production — an array or a date sliding past an === "object" check — and describe what you reach for instead when a value crosses a module or process boundary.
Frame the tradeoff: typeof is cheap, total and forgery-proof but almost information-free. Be ready to argue when a codebase should stop hand-rolling runtime type guards at all and push the check to a validated boundary instead of scattering them.
## What typeof actually is `typeof` is a unary operator, not a function, that evaluates its operand and returns one of a small fixed set of strings. Since BigInt landed in ES2020 the set is: `"undefined"`, `"boolean"`, `"number"`, `"bigint"`, `"string"`, `"symbol"`, `"object"`, and `"function"`. The rule is mechanical: every primitive gets its own string, every object gets `"object"` unless it is callable, in which case it gets `"function"`. There is no `"array"`, no `"date"`, no `"null"`, and no `"class"`. ```js typeof undefined // "undefined" typeof true // "boolean" typeof 42 // "number" typeof 10n // "bigint" typeof "hi" // "string" typeof Symbol() // "symbol" typeof {} // "object" typeof [] // "object" typeof null // "object" typeof function () {} // "function" typeof class {} // "function" ``` ## Why typeof null is "object" In the original implementation, values were stored as a small type tag plus a payload, and the tag `0` meant "object". The null value was represented as the machine null pointer, so its tag read as `0` and `typeof` reported `"object"`. It is a bug, not a design statement — null is a primitive, and the language has a separate Null type. A proposal to correct it to `"null"` was rejected because an enormous amount of deployed code branches on `typeof x === "object"` and then handles null inside that branch; changing the result would silently reroute all of it. The practical consequence is that `typeof` alone is never a sufficient object test: ```js function isObject(value) { return value !== null && typeof value === "object"; } ``` That predicate is still deliberately broad — it is true for arrays, dates, regexes and class instances, because all of those really are objects. ## Callables `"function"` is the one case where `typeof` looks past the value's type at an internal capability: any object with a `[[Call]]` internal method reports `"function"`. That covers function declarations, function expressions, arrow functions, methods, generator functions, async functions and `class` declarations. A class is a function object at runtime, so `typeof MyClass === "function"` — a common trip-up for people arriving from languages where a class is its own kind of entity. Correspondingly, `typeof callback === "function"` is the idiomatic and correct guard before invoking something a caller handed you. It is one of the few checks `typeof` is genuinely good at. ## Undeclared names, and the exception Reading an identifier that was never declared anywhere throws a ReferenceError. `typeof` is special-cased in the specification: if the operand is a plain identifier reference that cannot be resolved, the operator returns `"undefined"` instead of throwing. This is why pre-module feature detection looked like `if (typeof SomeGlobal !== "undefined")`. ```js console.log(typeof neverDeclaredAnywhere); // "undefined", no throw console.log(neverDeclaredAnywhere); // ReferenceError ``` The exception is a `let` or `const` binding touched before its declaration is evaluated. That binding *does* exist in the scope; it is merely uninitialised, so the reference resolves and then fails on the uninitialised check. `typeof` gets no free pass there and throws a ReferenceError. Note also that `typeof` says nothing about whether a *property* exists: `typeof obj.missing` is `"undefined"` for a property that is absent and for one explicitly set to `undefined`. Distinguishing those needs the `in` operator or `Object.hasOwn`. ## What typeof cannot do Because every non-callable object collapses to `"object"`, `typeof` cannot answer any of the questions people actually want answered: is this an array, a date, a Map, an instance of my class, a plain object? Each of those needs a different tool — a dedicated predicate, a brand read, or a prototype-chain test. Treat `typeof` as a primitive-vs-object-vs-callable filter, not as a type system. One last legacy oddity worth recognising: the specification's Annex B allows a host-provided legacy object whose `typeof` is `"undefined"` even though the value is an object (browsers use this for `document.all`). It exists purely to keep 1990s browser-sniffing code working, and you should never rely on it.
- Why was typeof null never corrected to return "null"?It was proposed and rejected. A vast amount of deployed code tests `typeof x === "object"` and handles the null case inside that branch. Returning `"null"` would silently push null values down a different path in code nobody is going to revisit, so the committee kept the bug. The workaround is trivial — compare with `=== null` — which further weakened the case for breaking the web.
- typeof missingGlobal returns "undefined" instead of throwing, so is typeof ever able to throw a ReferenceError?Yes. The free pass only covers an identifier that resolves to nothing at all. A `let` or `const` binding used before its declaration line does exist in the scope but is uninitialised, so `typeof x` on it throws a ReferenceError. The same applies to a class binding. Only genuinely undeclared names are safe.
- How would you distinguish a property that is absent from one that is present but set to undefined?`typeof` cannot: both give `"undefined"`. Use `"key" in obj`, which also sees inherited properties, or `Object.hasOwn(obj, "key")` (ES2022) for own properties only. `Object.keys` and destructuring likewise treat a present-but-undefined property as present, which is why default parameter values fire for it.
saying these in an interview costs you the question
- Says typeof null returns the string "null"
- Uses typeof to tell arrays apart from plain objects
- Thinks typeof NaN is "NaN" or "undefined"
- Claims typeof on any undeclared name always throws
- Believes typeof of a class is "object" or "class"