How typeof Works in JavaScript
The typeof operator in JavaScript is a quick runtime labeler, not a complete type checker. It is excellent for checking primitive values, useful for simple guards, and famously annoying for null, arrays, and NaN. Use it for the jobs it can actually do, then reach for more specific checks when the value needs more than a label.
typeof answers one narrow question: what type label does JavaScript give this value right now?
Basic typeof syntax
Put typeof before the value or expression you want to inspect:
typeof "hello"; // "string"
typeof 42; // "number"
typeof true; // "boolean"
You can use it with variables:
const username = "ada";
console.log(typeof username); // "string"
And with expressions:
console.log(typeof (10 / "oops")); // "number"
console.log(10 / "oops"); // NaN
That second example is the first useful warning: typeof tells you the broad JavaScript type, not whether the value is useful.
The typeof return values
typeof always returns a string.
| Value | Result |
|---|---|
"Ada" | "string" |
42 | "number" |
42n | "bigint" |
true | "boolean" |
undefined | "undefined" |
Symbol("id") | "symbol" |
function run() {} | "function" |
{} | "object" |
[] | "object" |
null | "object" |
There is no "array" result. There is no "null" result. There is no "integer" result. JavaScript had the chance to make this tidy and chose folklore instead.
For the primitive value list behind those labels, read JavaScript Primitive Values Explained.
typeof null returns object
This is the classic trap:
console.log(typeof null); // "object"
null is not an object. It is a primitive value that means intentional absence. The "object" result is a long-standing JavaScript bug from the language's earliest implementation, and it stays because changing it would break old code.
Check null directly:
function hasSelectedUser(user) {
return user !== null;
}
If a value might be either null or undefined, use a direct comparison that says what you mean:
function hasValue(value) {
return value !== null && value !== undefined;
}
Or use the concise loose check only when you intentionally want to catch both:
function isMissing(value) {
return value == null;
}
That is one of the rare places where == null has a clear purpose. It matches only null and undefined.
typeof NaN returns number
Another small joke with long reach:
const result = Number("hello");
console.log(result); // NaN
console.log(typeof result); // "number"
NaN means "not a number" as a result, but it still belongs to the number type.
This guard is incomplete:
function canUsePrice(price) {
return typeof price === "number";
}
console.log(canUsePrice(NaN)); // true
Use Number.isNaN() when you specifically need to reject NaN:
function canUsePrice(price) {
return typeof price === "number" && !Number.isNaN(price);
}
console.log(canUsePrice(19.99)); // true
console.log(canUsePrice(NaN)); // false
If you also need to reject Infinity, use Number.isFinite():
function isFiniteNumber(value) {
return typeof value === "number" && Number.isFinite(value);
}
Arrays are objects
This code disappoints almost everyone once:
const users = ["Ada", "Grace"];
console.log(typeof users); // "object"
Arrays are objects with special behavior. typeof only gives you the broad object label.
Use Array.isArray():
function countUsers(users) {
if (!Array.isArray(users)) {
return 0;
}
return users.length;
}
This is a common beginner bug:
function countUsers(users) {
if (typeof users !== "array") {
return 0;
}
return users.length;
}
That condition is always true because typeof users can never be "array". The function returns 0 even for real arrays. The code looks reasonable right up until reality arrives with a clipboard.
Functions get their own label
Functions are objects, but typeof gives callable functions a special result:
function greet() {}
console.log(typeof greet); // "function"
That makes typeof useful before calling a callback:
function runTask(task, onDone) {
const result = task();
if (typeof onDone === "function") {
onDone(result);
}
return result;
}
It does not prove the function is safe, pure, async, or shaped the way you want. It only proves the value is callable enough for JavaScript to label it "function".
typeof works on undeclared names, with one catch
typeof can inspect a name that was never declared:
console.log(typeof missingValue); // "undefined"
That can be useful in old scripts or feature checks where reading the variable directly would throw a ReferenceError.
But typeof does not bypass the temporal dead zone for let and const:
console.log(typeof token); // ReferenceError
let token = "abc";
If a let or const binding exists later in the same scope, the name is known but not initialized yet. typeof cannot rescue you there.
Good uses for typeof
typeof is best when you need to branch on primitive values:
function formatDisplayValue(value) {
if (typeof value === "string") {
return value.trim();
}
if (typeof value === "number") {
return value.toFixed(2);
}
if (typeof value === "boolean") {
return value ? "Yes" : "No";
}
return "";
}
It is also useful for defaults:
function createPageSize(input) {
if (typeof input !== "number" || !Number.isFinite(input)) {
return 20;
}
return input;
}
Notice the second check. The typeof guard gets you into the numeric neighborhood. Number.isFinite() checks whether the number is usable for this purpose.
When typeof is not enough
Use a more specific check when the question is more specific than the broad type label.
| Question | Better check |
|---|---|
Is this value null? | value === null |
| Is this an array? | Array.isArray(value) |
Is this NaN? | Number.isNaN(value) |
| Is this a finite number? | Number.isFinite(value) with a typeof guard |
| Is this a date object? | value instanceof Date and !Number.isNaN(value.getTime()) |
| Is this a plain object? | Check value !== null, typeof value === "object", and the prototype if needed |
For truthy checks and boolean coercion, see Truthy and Falsy Values. typeof and truthiness answer different questions. Mixing them up is how 0, "", and NaN sneak into bugs.
A practical type guard pattern
Here is a small guard for a settings object:
function isSettings(value) {
return (
value !== null &&
typeof value === "object" &&
!Array.isArray(value) &&
typeof value.theme === "string" &&
typeof value.notifications === "boolean"
);
}
console.log(isSettings({ theme: "dark", notifications: true })); // true
console.log(isSettings(null)); // false
console.log(isSettings([])); // false
console.log(isSettings({ theme: "dark" })); // false
The order matters. Check value !== null before treating it like an object. Check Array.isArray() because arrays are objects too. Then check the fields.
typeof is part of the guard. It is not the whole guard.
Key Takeaways
typeofreturns a string label such as"string","number","boolean","undefined","bigint","symbol","function", or"object".typeof nullreturns"object"because of a historical bug. Checknulldirectly.- Arrays, dates, regular expressions, and plain objects all return
"object", so use more specific checks when the shape matters. typeof NaNreturns"number". UseNumber.isNaN()orNumber.isFinite()for numeric validity.typeofis a good first filter, not a complete validation system.