· tips · 5 min read
Unlocking the Mysteries of the typeof Operator: JavaScript Demystified
A deep, practical guide to JavaScript's typeof operator: what it tells you, where it lies, its surprising results (like typeof null === 'object'), and robust patterns to reliably detect types in real-world code.

Why typeof matters
Type checks are everywhere in JavaScript: runtime feature detection, guarding code paths, input validation, logging, or branching behavior. The typeof
operator is the most lightweight, built-in tool for checking the type of a value. But it has quirks and historical oddities that cause confusion - and bugs - if you’re not careful.
This post will explain exactly what typeof
does, why some of its results seem surprising, and how to combine it with other techniques to get reliable type information.
The basics: how typeof works
typeof
is an operator that returns a string describing the high-level type category of a value. Example:
typeof 42; // "number"
typeof 'hello'; // "string"
typeof true; // "boolean"
typeof undefined; // "undefined"
typeof Symbol(); // "symbol" // ES6+
typeof 10n; // "bigint" // ES2020+
typeof function () {}; // "function"
typeof {}; // "object"
Notes:
- For primitive values (
number
,string
,boolean
,symbol
,bigint
,undefined
)typeof
returns the expected primitive type name. - Functions return the string
"function"
(technically a callable object but singled out bytypeof
). - Most other non-primitive values return
"object"
.
Reference: MDN’s typeof documentation is a great starting point: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
The famous gotcha: typeof null === ‘object’
One of the most notorious surprises is:
typeof null; // "object"
This is a historical quirk that dates back to the original implementation of JavaScript: null
was represented with a type tag that made the operator return "object"
. That behavior was never changed due to backward compatibility. The ECMAScript spec keeps this behavior for compatibility reasons.
Implication: never rely on typeof
alone to check for null
. Instead:
value === null;
// or
value == null; // checks for null or undefined (if you actually need both)
MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof#null
Arrays, Dates, RegExps and other objects
typeof
classifies arrays, dates and regexps as "object"
:
typeof []; // "object"
typeof new Date(); // "object"
typeof /a/; // "object"
To distinguish these types reliably use:
Array.isArray(value)
for arraysvalue instanceof Date
for dates (may fail across realms/iframes)value instanceof RegExp
for regexpsObject.prototype.toString.call(value)
for a robust signature
Example using Object.prototype.toString.call
:
Object.prototype.toString.call([]); // "[object Array]"
Object.prototype.toString.call(new Date()); // "[object Date]"
Object.prototype.toString.call(null); // "[object Null]"
This is the basis for a reliable getType
helper (see below).
MDN on Object.prototype.toString: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
Functions and callable objects
typeof
returns "function"
for normal functions, arrow functions, generator functions, async functions, and even class declarations (classes are special functions):
typeof (() => {}); // "function"
typeof function* () {}; // "function"
typeof async function () {}; // "function"
typeof class MyClass {}; // "function"
This makes typeof
useful for checking whether a value is callable.
Wrapper objects vs primitives
JavaScript also has wrapper objects like new Number(1)
and new String('x')
:
typeof 1; // "number"
typeof new Number(1); // "object"
typeof 'a'; // "string"
typeof new String('a'); // "object"
Prefer primitives over wrapper objects. If you must detect wrappers, typeof
helps show the distinction.
Undeclared variables and safety
A useful property of typeof
is that it does not throw for undeclared identifiers. Consider browser feature detection:
// If 'SomeGlobal' is not declared at all, this won't throw
if (typeof SomeGlobal !== 'undefined') {
// safe to reference SomeGlobal now
}
If you try to directly reference an undeclared variable you’ll get a ReferenceError; typeof
avoids that.
More surprising results and edge cases
typeof NaN
->"number"
NaN
is a numeric value andtypeof
treats it asnumber
.
typeof Infinity
->"number"
typeof /a/
->"object"
(regexps are objects)- Typed arrays (e.g.
new Int8Array()
) are"object"
- Host objects (e.g. some DOM objects) might behave slightly differently across environments - prefer feature detection.
Also: older environments won’t know symbol
or bigint
. Feature-detect before using:
if (typeof Symbol !== 'undefined') {
// safe to use symbols
}
A reliable getType helper
If you need more precise, human-friendly type strings across all built-in types, here’s a small utility that combines typeof
and Object.prototype.toString
:
function getType(value) {
// quick primitive handling
const t = typeof value;
if (t !== 'object') return t; // covers 'undefined', 'string', 'number', 'boolean', 'function', 'symbol', 'bigint'
if (value === null) return 'null';
// For objects, use Object.prototype.toString
const tag = Object.prototype.toString.call(value); // e.g. "[object Array]"
return tag.slice(8, -1).toLowerCase(); // "array", "date", "regexp", "object" etc.
}
// Examples
getType(1); // "number"
getType(null); // "null"
getType([]); // "array"
getType(new Date()); // "date"
getType(/a/); // "regexp"
getType(function () {}); // "function"
This helper gives you a consistent, descriptive type name that handles the null
quirk and disambiguates arrays, dates, and more.
Best practices and checklist
- Use
typeof
for quick primitive checks and safe feature detection (e.g.typeof Symbol !== 'undefined'
). - Don’t use
typeof
alone to test fornull
- usevalue === null
. - Use
Array.isArray(value)
to detect arrays instead oftypeof
. - Use
Object.prototype.toString.call(value)
(or thegetType
helper above) for robust type detection when precise classification matters. - Prefer primitives over wrapper objects (
new Number
,new String
) - they confuse bothtypeof
and equality checks. - For checking callable values,
typeof value === 'function'
is fine. - For
NaN
detection useNumber.isNaN(value)
(orNumber.isFinite
for finite numbers) rather thantypeof
.
Quick examples:
// Good
if (typeof x === 'string') {
/* primitive string */
}
if (Array.isArray(arr)) {
/* array */
}
if (value === null) {
/* null */
}
if (typeof maybeFn === 'function') {
maybeFn();
}
// Avoid
if (typeof x === 'object' && x) {
/* ambiguous: could be array, date, null was excluded by x truthiness */
}
When you should not use typeof
- To distinguish between objects like arrays vs objects vs dates -
typeof
is too coarse. - To check for
null
- it lies. - For cross-realm (iframes, workers) checks using
instanceof
-instanceof
can fail; preferObject.prototype.toString.call
there.
Summary
typeof
is a compact, fast operator that works well for many everyday checks: primitives, function detection, and safe feature detection for undeclared globals. However, it is intentionally coarse for objects and has a well-known historical quirk (typeof null === 'object'
). For robust, precise type detection use Array.isArray
, instanceof
(when appropriate), and Object.prototype.toString.call
(or a small getType
helper) to complement typeof
.
Further reading
- MDN: typeof operator - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof
- MDN: Object.prototype.toString - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
- MDN: Array.isArray - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray