The typeof
statement is useful in JavaScript for data validation and type checking, but it has some odd features to be aware of.
There are two instances when typeof
returns "object"
in an unexpected way, and this makes type checking a little unusual.
What Is the Typeof Statement in JavaScript?
Typeof
is a statement that’s used in JavaScript to check the type variable in your code. It can return one of JavaScript’s eight data types, and it’s especially useful for returning most of the primitive types in JavaScript, including undefined
, string
and number
.
Both typeof
null
and typeof
an array return "object"
in a potentially misleading way, as null
is a primitive type (not an object), and arrays are a special, built-in type of object in JavaScript.
In this article, I examine every possible result of typeof
in JavaScript.
Data Types
While there are only eight data types (seven primitives and objects) in JavaScript, typeof
will actually return one of nine options:
undefined
object
(meaningnull
)boolean
number
bigint
string
symbol
function
object
(meaning any object, including arrays)
These correspond to the JavaScript data types, with the exception that the typeof
null
is also "object"
due to a long-standing bug.
Next, I explain when to expect each response from typeof
in detail.
Undefined
An undefined
value in JavaScript is pretty common. It means a variable name has been reserved, but no value has been assigned to that reference yet. It’s not defined yet, so we call it undefined.
The value undefined
is a primitive type in JavaScript, and undeclared variables are also considered to be undefined.
Referencing undeclared variables usually results in a ReferenceError, except when using the typeof
keyword.
The typeof
undefined
is the string "undefined"
— and undefined is a falsy value that is loosely equal to null
but not to other falsy values.
// The value undefined is loosely equals to null but not other falsy values:
console.log(undefined === undefined) // true
console.log(undefined === null) // false
console.log(undefined == null) // true
console.log(Boolean(undefined)) // false
console.log(undefined == false) // false
// The typeof keyword returns "undefined" for the value undefined:
console.log(typeof undefined) // undefined
// A declared variable that has not been assigned a value is undefined by default:
let undefinedVariable
console.log(undefinedVariable) // undefined
console.log(typeof undefinedVariable) // undefined
// The typeof an undeclared variable is undefined, making it a safe way to check if a variable has been declared:
console.log(typeof undeclaredVariable)
// Referencing an undeclared variable without typeof will throw a ReferenceError:
try { undeclaredVariable } catch(e) { console.log(e) } // ReferenceError: undeclaredVariable is not defined
If typeof
says a value is "undefined"
, then it’s a safe bet to assume that value is actually undefined
, meaning it was not declared, declared but never assigned a value or declared and assigned the value of undefined
.
Null Object
For historical reasons, the typeof
null
in JavaScript is "object"
. This is a bug that is expected to never be fixed in JavaScript.
That means that checking for null cannot be performed using typeof
.
However, null checking is pretty easy using the strict equality operator (===
) to check that the value is indeed null
, as in maybeNull===null
.
Useful Things to Know About Null
- The value
null
is falsy (evaluates to false in a conditional). - The values
null
andundefined
are only loosely equal to each other. - Neither
null
norundefined
are equal to other falsy values.
This means that checking for null using the loose equality (==
) operator will capture both null and undefined values, which can be useful:
console.log(null) // null
console.log(typeof null) // "object"
console.log(null === null) // true
console.log(null === undefined) // false
console.log(null == undefined) // true
console.log(null == false) // false
console.log(Boolean(null)) // false
// Alternatively, null is the only falsy object
console.log(!null && typeof null === "object") // true
isNull = (value) => !value && typeof value === "object"
console.log(isNull(null)) // true
Often, an undefined
value means the same thing as a null
value — the absence of a value — so, using ==
is recommended to check for null.
On the other hand, checking for null can be performed easily with the strict equality ===
operator.
Or one can check by knowing that since the empty object is truthy (evaluates to Boolean true
in a conditional), null
is the only falsy object.
Boolean
Checking for Boolean values is easy. They are going to be either true
or false
, and the typeof
a boolean returns "boolean"
:
console.log(typeof true) // boolean
console.log(typeof false) // boolean
// The Boolean() wrapper will convert truthy and falsy values:
console.log(typeof Boolean(37)) // boolean
console.log(typeof Boolean(0)) // boolean
// Two exclamation points !! (the logical NOT) operator are equivalent to Boolean()
console.log(typeof !!(37)) === // boolean
console.log(typeof !!(0)) === // boolean
// Conditionals will coerce values to boolean in the same way:
37 ? console.log("truthy") : console.log("falsy") // truthy
0 ? console.log("truthy") : console.log("falsy") // falsy
Note that JavaScript will coerce any value to true
or false
by using the Boolean() wrapper function, which puts two exclamation points (the logical NOT — !
) in front of the expression. Or it’ll put the statement inside of a conditional, such as an if statement, question mark ?
operator, or loop.
The values that evaluate to false
are called the falsy values, and everything else in JavaScript evaluates to true
and is a truthy value.
The falsy values in JavaScript are false
, 0
, 0n
, null
, undefined
, NaN
and the empty string “”
. Everything else is truthy.
Number
Checking for a number in JavaScript works as expected, with typeof
returning "number"
:
console.log(typeof 37) // "number"
console.log(typeof 2.71828) // "number"
console.log(typeof Math.E) // "number"
console.log(typeof Infinity) // "number"
// The typeof NaN is "number" even though NaN means "Not-A-Number":
console.log(typeof NaN) // "number"
// Calling Number explicitly is one way to parse a number:
console.log(typeof Number(`1`)) // "number"
// The parseInt and parseFloat functions are other ways to parse:
console.log(typeof parseInt(`100`)) // "number"
console.log(typeof parseFloat(`100.01`)) // "number"
// Parse failures lead to NaN, and NaN poisons other math:
console.log(typeof (2 * parseInt(`invalid`))) // "number"
Note that this means that checking for NaN
requires checking for self-equality because NaN
is the only value in JavaScript that doesn’t equal itself:
const mightBeNaN = NaN // NaN means "Not-a-Number"
// Anything compares false when compared to NaN:
console.log(37 === NaN) // false
console.log(mightBeNaN === NaN) // false
// NaN is the only value that does not equal itself:
console.log(mightBeNaN !== mightBeNaN) // true
// Creating an invalid Date results in the value NaN:
console.log(new Date(`invalid`) !== new Date(`invalid`)) // true
// For improved code readability, some developers prefer Number.isNan():
console.log(Number.isNan(mightBeNaN)) // true
BigInt
Checking for the primitive type BigInt
works as expected; typeof
for supported browsers will return "bigint"
:
console.log(typeof 37n) // bigint
“BigInt is a built-in object that provides a way to represent whole numbers larger than 253 - 1, which is the largest number JavaScript can reliably represent with the Number primitive. BigInt can be used for arbitrarily large integers,” according to the MDN web documentation.
Officially, BigInt
was added to modern JavaScript as part of ES11 (ECMAScript 2020), and it is supported by Chrome, and thus by Node.js, Firefox and Edge.
String
As one might hope, checking for a string in JavaScript is pretty straight-forward. typeof
works exactly as expected, returning "string"
:
console.log(typeof '37') // string
console.log(typeof "37") // string
console.log(typeof `37`) // string
// Note that typeof always returns a string:
console.log(typeof (typeof 37)) // string
// The String() wrapper function converts anything into a string:
console.log(String(37))
It sure is nice when things work like they should when programming.
Symbol
The primitive data type symbol is a unique identifier, useful for creating keys on objects in JavaScript.
“A symbol value may be used as an identifier for object properties; this is the data type’s only purpose,” according to MDN web docs.
As one would expect, the typeof Symbol()
is indeed "symbol"
:
console.log(typeof Symbol()) // symbol
console.log(typeof Symbol(37)) // symbol
console.log(typeof Symbol.iterator) // symbol
const symbolUno = Symbol()
const symbolDos = Symbol(37)
const symbolTres = Symbol("37")
console.log(typeof symbolUno) // symbol
console.log(String(symbolTres)) // Symbol(37)
// Every symbol value returned from Symbol() is unique:
console.log(Symbol() === Symbol()) // false
console.log(Symbol(37) === Symbol(37)) // false
console.log(Symbol("37") === Symbol("37")) // false
Function
Functions are easily checked for using the typeof
keyword, which works entirely as expected by returning "function"
:
console.log(typeof function myFunction() {}) // function
console.log(typeof class myClass {}) // function
console.log(typeof (() => {})) // function
// This includes built-in functions, for example Number.isNaN():
console.log(typeof Number.isNaN) // "function"
// But not properties, of course:
console.log(typeof "".length) // "number"
// And calling a function will check the typeof the return value:
console.log(typeof Number.isNaN()) // "boolean"
console.log(typeof Number.isNaN(37)) // "boolean"
Object (Meaning Object or Array)
As long as the value in question is not null, typeof
returning "object"
means that the JavaScript value is a JavaScript object.
One type of object that is built into JavaScript is the array, and the typeof
of an array is "object"
: typeof [] === `object` // true
.
ECMAScript 5 introduced an Array.isArray()
method to check for an array, since typeof
will not be able to tell arrays from other objects.
The JavaScript prototypes Date
and RegExp
are two other types of built-in objects where typeof
returns “object.” Thus, dates and regular expressions need more differentiation than just using the typeof
keyword.
Here’s how to check the type of objects and arrays:
const helloWorldObject = { hello: "world" }
console.log(typeof helloWorldObject) // 'object'
// use Array.isArray or Object.prototype.toString.call
// to differentiate regular objects from arrays
const fibonacciArray = [1, 1, 2, 3, 5, 8]
console.log(typeof fibonacciArray) // 'object'
console.log(Array.isArray(helloWorldObject)) // false
console.log(Array.isArray(fibonacciArray)) // true
// There is another helper function, though it is a bit long:
console.log(Object.prototype.toString.call(helloWorldObject)) // [object Object]
console.log(Object.prototype.toString.call(fibonacciArray)) // [object Array]
// Regular expression have their own native object, RegExp
const myRegExp = /search/
console.log(typeof myRegExp) // 'object'
console.log(myRegExp instanceof RegExp) // true
console.log(Object.prototype.toString.call(myRegExp)) // [object RegExp]
// The Date native object is built-in to JavaScript
const emptyDate = new Date()
const invalidDate = new Date("fail")
console.log(typeof emptyDate) // 'object'
console.log(typeof invalidDate) // 'object'
// Checking for a date is a little trickier
console.log(emptyDate instanceof Date)
console.log(invalidDate instanceof Date)
console.log(Object.prototype.toString.call(invalidDate)) // [object Date]
// Reliable date checking requires a NaN check by checking for NaN:
console.log(invalidDate instanceof Date && !Number.isNaN(invalidDate.valueOf())) // true
The verbose JavaScript statement Object.prototype.toString.call()
can differentiate between generic objects, arrays and other objects because it returns a string that specifies the object type in more detail than typeof
.
Similarly, the helper method Array.isArray()
or the keyword instanceof
can be used to check for arrays or any type of object, respectively.
Typeof and Object Wrappers Explained
The object wrappers for Boolean, number and string will break typeof
and result in "object"
instead of "boolean"
, "number"
, or "string"
.
// "The following are confusing, dangerous, and wasteful. Avoid them." -MDN Docs
typeof new Boolean(false) === 'object'; // true
typeof new Number(37) === 'object'; // true
typeof new String(`Hello World!`) === 'object'; // true
Why do these object wrappers exist if they should not be called explicitly?
Basically, calling a string property like "".length
results in JavaScript making a wrapper object and interpreting the code this way:
"".length === String("").length // true
Since that happens automatically, there is no need to create an explicit wrapper object, as it will only break typeof
, as shown above.
Why You Shouldn’t Use Wrapper Objects
Similarly, wrapper objects change the function of the ==
and ===
equality operators in JavaScript.
And the behavior only actually changes when the new keyword is used with the object wrapper call, as is shown in the following example:
const primitiveString = `Hello world!` // primitive type string
const wrappedString = new String(`Hello world!`) // wrapper object
console.log(typeof primitiveString) // "string"
console.log(typeof wrappedString) // "object"
console.log(primitiveString == wrappedString) // true
console.log(primitiveString === wrappedString) // false
const almostWrappedString = String(`Hello world!`) // wrapper called without new
console.log(typeof almostWrappedString) // "string"
console.log(primitiveString === almostWrappedString) // true
Generally speaking, the string, Boolean and number wrappers will be called automatically and should not be called by JavaScript developers.
Typeof and Host Objects in JavaScript
Host objects are implementation-dependent, meaning they are supplied by the local run-time environment.
Put another way, host objects are special objects beyond the standard built-in objects or native objects JavaScript provides.
For example, the window
object is supplied by the browser-environment supplies, while a node.js environment supplies other host objects.
This means that the typeof
keyword is also implementation-dependent, at least in the case of host objects.
That being said, modern implementations are probably going to return “object” as the typeof
host objects. For example:
typeof window // "object"
Are Static Types the Future in JavaScript?
Since JavaScript is a dynamically typed language but typeof
has its quirks, some developers prefer automatic or static type checking using a language like TypeScript or a static type checker like Flow.
Using TypeScript or Flow definitely has some advantages and can prevent bugs, but at the cost of learning and implementing another tool.
While useful, using either TypeScript or Flow will add a whole new layer of complexity to your JavaScript programming.
For vanilla JavaScript, mastering typeof
is all you need in order to check data types like a champion.
Advantages of Typeof in JavaScript
The keyword typeof
is useful for type checking in JavaScript, but it has some caveats associated with historical bugs.
Type checking with typeof
is especially useful for most of the primitive types in JavaScript, including undefined
, string
and number
.
On the other hand, Array
, Date
and Regular Expressions
are native objects that are not differentiated from each other by typeof
. They all return "object"
— as does null
, unexpectedly in a well-known bug.
In short, typeof
is an imperfect but powerful tool to check a value’s type.