typeof
: Understanding type checking in JavaScriptA very important aspect of every programming language is its type system and data types. For a strictly typed programming language like Java, variables are defined to be of a particular type, constraining the variable to only contain values of that type.
JavaScript, however, is a dynamically typed language, although some extensions exist that support strict typing, such as TypeScript.
With JavaScript, it is possible to have a variable that started off as containing a string
, and much later in its lifecycle, has become a reference to an object
. There are even times when the JavaScript engine implicitly coerces the type of a value during script execution. Type checking is very critical to writing predictable JavaScript programs.
JavaScript has a pretty basic
typeof
operator for the purpose of type checking.
However, you will notice that using this operator could be misleading, as we will discuss in this article.
Before looking at type checking with typeof
, it is important to have a glance at the JavaScript data types. Although this article does not go into details about the JavaScript data types, you can glean a thing or two as you progress.
Prior to ES6, JavaScript had six data types. In the ES6 specification, the Symbol
type was added. Here is a list of all the types:
true
and false
)null
)undefined
)The first six data types are referred to as primitive types. Every other data type besides these first six is an object and may be referred to as a reference type. An object type is simply a collection of properties in the form of name and value pairs.
Notice from the list that null
and undefined
are primitive JavaScript data types, each being a data type containing just one value.
You may begin to wonder: Wwhat about arrays, functions, regular expressions, etc? They are all special kinds of objects.
array
is a special kind of object that is an ordered collection of numbered values with special syntax and characteristics that makes working with it different from with regular objects.function
is a special kind of object that has an executable script block associated with it. The script block is executed by invoking the function. It also has a special syntax and characteristics that makes it different from other regular objects.JavaScript has several object class constructors for creating other kinds of objects such as:
Date
 — for creating date objectsRegExp
 — for creating regular expressionsError
 — for creating JavaScript errorstypeof
The typeof
operator in JavaScript is a unary operator(takes only one operand) that evaluates to a string indicating the type of its operand. Just like other unary operators, it is placed before its operand separated by a space:
typeof 53; // "number"
However, there is an alternative syntax that allows you to use typeof
like a function invocation by wrapping its operand in parentheses. This is very useful for type checking the value returned from JavaScript expressions:
typeof(typeof 53); // "string"
Prior to ES6, the typeof
operator always returns a string irrespective of the operand it is used on.
For undeclared identifiers,
typeof
will return“undefined”
instead of throwing aReferenceError
.
console.log(undeclaredVariable === undefined); // ReferenceError console.log(typeof undeclaredVariable === 'undefined'); // tru
However, in ES6, block-scoped variables declared using the let
or const
keywords will still throw a ReferenceError
if they are used with the typeof
operator before they are initialized. This is because:
Block-scoped variables remain in the temporal dead zone until they are initialized:
// Before block-scoped identifier: typeof => ReferenceError console.log(typeof tdzVariable === 'undefined'); // ReferenceError const tdzVariable = 'I am initialized.';
The following code snippet shows type checks for common values using the typeof
operator:
console.log(typeof ""); // "string" console.log(typeof "hello"); // "string" console.log(typeof String("hello")); // "string" console.log(typeof new String("hello")); // "object" console.log(typeof 0); // "number" console.log(typeof -0); // "number" console.log(typeof 0xff); // "number" console.log(typeof -3.142); // "number" console.log(typeof Infinity); // "number" console.log(typeof -Infinity); // "number" console.log(typeof NaN); // "number" console.log(typeof Number(53)); // "number" console.log(typeof new Number(53)); // "object" console.log(typeof true); // "boolean" console.log(typeof false); // "boolean" console.log(typeof new Boolean(true)); // "object" console.log(typeof undefined); // "undefined" console.log(typeof null); // "object" console.log(typeof Symbol()); // "symbol" console.log(typeof []); // "object" console.log(typeof Array(5)); // "object" console.log(typeof function() {}); // "function" console.log(typeof new Function); // "function" console.log(typeof new Date); // "object" console.log(typeof /^(.+)$/); // "object" console.log(typeof new RegExp("^(.+)$")); // "object" console.log(typeof {}); // "object" console.log(typeof new Object); // "object"
Notice that all object type constructor functions, when instantiated with the new
keyword will always have a type of “object”
. The only exception to this is the Function
constructor.
Here is a simple summary of the results:
value | typeof |
---|---|
undefined |
"undefined" |
null |
"object" |
true or false |
"boolean" |
all numbers or NaN |
"number" |
all strings | "string" |
all symbols | "symbol" |
all functions | "function" |
all arrays | "object" |
native objects | "object" |
host objects | dependent on implementation |
other objects | "object" |
The type check results from the previous section indicate that some values will require additional checks to further distinguish them. For example: null
and []
will both be of “object”
type when the type check is done using the typeof
operator.
The additional checks on the value can be done by leveraging on some other characteristics:
instanceof
operatorconstructor
property of the objecttoString()
method of the objectUsing the typeof
operator to check for a “null”
value does no good, as you have already seen. The best way to check for a “null”
value is to do a strict equality comparison of the value against the null
keyword as shown in the following code snippet.
function isNull(value) { return value === null; }
The use of the strict equality operator(===
) is very important here. The following code snippet illustrates this importance using the undefined
value:
console.log(undefined == null); // true console.log(undefined === null); // false
NaN
is a special value received when arithmetic operations result in values that are undefined cannot be represented. For example: (0 / 0) => NaN
. Also, when an attempt is made to convert a non-numeric value that has no primitive number representation to a number, NaN
is the result.
Any arithmetic operation involving
NaN
will always evaluate toNaN
.
If you really want to use a value for any form of arithmetic operation then you want to be sure that the value is not NaN
.
Using the typeof
operator to check for NaN
value returns “number”
. To check for NaN
value, you can use the global isNaN()
function, or preferably the Number.isNaN()
function added in ES6:
console.log(isNaN(NaN)); // true console.log(isNaN(null)); // false console.log(isNaN(undefined)); // true console.log(isNaN(Infinity)); // false console.log(Number.isNaN(NaN)); // true console.log(Number.isNaN(null)); // false console.log(Number.isNaN(undefined)); // false console.log(Number.isNaN(Infinity)); // false
The
NaN
value has a very special characteristic. It is the only JavaScript value that is never equal to any other value by comparison, including itself:
var x = NaN; console.log(x == NaN); // false console.log(x === NaN); // false
You can check for NaN
as follows:
function isNan(value) {
return value !== value;
}
The above function is very similar to the implementation of Number.isNaN()
added in ES6 and hence can be used as a polyfill for non-ES6 environments as follows:
Number.isNaN = Number.isNaN || (function(value) { return value !== value; })
Finally, you can leverage on the Object.is()
function added in ES6 to test if a value is NaN
. The Object.is()
function checks if two values are the same value:
function isNan(value) { return Object.is(value, Number.NaN); }
Using typeof
to check for an array will return “object”
. There are several ways to better check for an array as shown in this code snippet:
// METHOD 1: constructor property // Not reliable function isArray(value) { return typeof value == 'object' && value.constructor === Array; } // METHOD 2: instanceof // Not reliable since an object's prototype can be changed // Unexpected results within frames function isArray(value) { return value instanceof Array; } // METHOD 3: Object.prototype.toString() // Better option and very similar to ES6 Array.isArray() function isArray(value) { return Object.prototype.toString.call(value) === '[object Array]'; } // METHOD 4: ES6 Array.isArray() function isArray(value) { return Array.isArray(value); }
As seen with arrays, the Object.prototype.toString()
method can be very useful for checking the object type of any JavaScript value. When it is invoked on a value using call()
or apply()
, it returns the object type in the format: [object Type]
, where Type
is the object type.
Consider the following code snippet:
function type(value) { var regex = /^[object (S+?)]$/; var matches = Object.prototype.toString.call(value).match(regex) || []; return (matches[1] || 'undefined').toLowerCase(); }
The following code snippet shows results of type checking using the just created type()
function:
console.log(type('')); // "string" console.log(type('hello')); // "string" console.log(type(String('hello'))); // "string" console.log(type(new String('hello'))); // "string" console.log(type(0)); // "number" console.log(type(-0)); // "number" console.log(type(0xff)); // "number" console.log(type(-3.142)); // "number" console.log(type(Infinity)); // "number" console.log(type(-Infinity)); // "number" console.log(type(NaN)); // "number" console.log(type(Number(53))); // "number" console.log(type(new Number(53))); // "number" console.log(type(true)); // "boolean" console.log(type(false)); // "boolean" console.log(type(new Boolean(true))); // "boolean" console.log(type(undefined)); // "undefined" console.log(type(null)); // "null" console.log(type(Symbol())); // "symbol" console.log(type(Symbol.species)); // "symbol" console.log(type([])); // "array" console.log(type(Array(5))); // "array" console.log((function() { return type(arguments) })()); // "arguments" console.log(type(function() {})); // "function" console.log(type(new Function)); // "function" console.log(type(class {})); // "function" console.log(type({})); // "object" console.log(type(new Object)); // "object" console.log(type(/^(.+)$/)); // "regexp" console.log(type(new RegExp("^(.+)$"))); // "regexp" console.log(type(new Date)); // "date" console.log(type(new Set)); // "set" console.log(type(new Map)); // "map" console.log(type(new WeakSet)); // "weakset" console.log(type(new WeakMap)); // "weakmap"
It is very possible that at one point or the other, you may have come across this statement:
“Everything in JavaScript is an object.” — (False)
This could be very misleading and as a matter of fact, it is not true. Everything in JavaScript is not an object. Primitives are not objects.
You may begin to wonder — why then can we do the following kinds of operations on primitives if they are not objects?
(“Hello World!”).length
 — getting length
property of the string(“Another String”)[8]
—  getting the character of the string at index 8
(53.12345).toFixed(2)
— calling Number.prototype.toFixed()
method on the numberThe reason why we can achieve these with primitives is because the JavaScript engine implicitly creates a corresponding wrapper object for the primitive and invokes the method or accesses the property on it.
When the value has been returned, the wrapper object is discarded and removed from memory. For the operations listed earlier, the JavaScript engine implicitly does the following:
// wrapper object: new String("Hello World!") (new String("Hello World!")).toLowerCase(); // wrapper object: new String("Another String") (new String("Another String"))[8]; // wrapper object: new Number(53.12345) (new Number(53.12345)).toFixed(2);
In this article, you have been taken through a pinch of the JavaScript type system and its data types, and how type checking can be performed using the typeof
operator.
You also saw how misleading type checking can be, using the typeof
operator. And finally, you saw several ways of implementing predictable type checking for some data types.
If you are interested in getting some additional information about the JavaScript typeof
operator, you can refer to this article.
Happy coding…
Debugging code is always a tedious task. But the more you understand your errors, the easier it is to fix them.
LogRocket allows you to understand these errors in new and unique ways. Our frontend monitoring solution tracks user engagement with your JavaScript frontends to give you the ability to see exactly what the user did that led to an error.
LogRocket records console logs, page load times, stack traces, slow network requests/responses with headers + bodies, browser metadata, and custom logs. Understanding the impact of your JavaScript code will never be easier!
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.
One Reply to "JavaScript <code>typeof</code>: Understanding type checking in JavaScript"
Thank you for your blog. It has been very useful to me to understand “typing” in JavaScript.
However your getType(value) function in “Generic type-checking” is not working, the regex expression is incorrect. You should replace it by
let regex = /^\[object\s(\S+?)\]$/;