As an untyped language, JavaScript necessitates dynamic type checking. To strictly enforce type for every variable would hamper much of the language’s power, but when necessary, understanding the different ways to check type will make our programs better. There are many ways to type check in JavaScript, and I will go over four1 in this post, discussing nuances of each.

typeof operator

The typeof operator in JavaScript returns a string describing the type of any value its given. Its implementation is standard and used widely, defined in the first edition of ECMAScript2. It has six possible string values in ECMAScript 53.

typeof 'str';        // "string"
typeof 10;           // "number"
typeof true;         // "boolean"
typeof {};           // "object"
typeof function(){}; // "function"
typeof undefined;    // "undefined"

The consistency and reliability of this operator is terrific, but we run into problems for anything that isn’t one of the above types, including arrays, null, and custom objects. Furthermore, type checking this way requires that we compare strings, which is sensitive to case and spelling (and generally gives me the heebie jeebies).

typeof null;                     // "object"
typeof [];                       // "object"
typeof new String('str');        // "object"
typeof new function Custom(){};  // "object"

typeof {} === 'Object';          // false
typeof {} === 'objcet';          // false

instanceof operator

The instanceof operator compares two values—a variable and a function—by checking the variable’s prototype chain for the function object’s prototype. Because we can provide any function we want as the second parameter, instanceof can be used check custom types. Furthermore, instanceof uses a function instead of a string, so our program will let us know if we’ve made any mistakes.

{} instanceof Object;               // true
[] instanceof Array;                // true
(function(){}) instanceof Function; // true

var Foo = function(){};
new Foo() instanceof Foo;           // true

new Foo() instanceof Doo;           // ReferenceError: Doo is not defined

Checking an object’s prototype chain is very helpful, but instanceof will always return false for primitive types (strings, numbers, booleans, null, and undefined) because they are not objects and thus have no prototype.

'str' instanceof String;        // false
9 instanceof Number;            // false
true instanceof Boolean;        // false
null instanceof Object;         // false
undefined instanceof Object;    // false

Although we cannot pass null or undefined as right-hand values (since they aren’t functions), we can easily check for null and undefined with an equivalence comparison.

undefined instanceof undefined; // TypeError: Expecting a function in instanceof check, but got undefined
null instanceof null;           // TypeError: Expecting a function in instanceof check, but got null

undefined === undefined;        // true
null === null;                  // true

constructor property

Objects inherit the constructor property from their prototype, which we can use to check a variable’s type without going down the whole prototype chain. Like instanceof, we are checking with function objects, so we can check for custom functions.

({}).constructor === Object;           // true
/regex/.constructor === RegExp;        // true
(new Date()).constructor === Date;     // true

[].constructor === Array;              // true
[] instanceof Array;                   // true
[].constructor === Object;             // false
[] instanceof Object;                  // true

var Custom = function(){};
(new Custom()).constructor === Custom; // true

This also works for primitive numbers, strings, and booleans, because JavaScript will wrap these in Number, String, and Boolean when we attempt to access a property on them.

(9).constructor === Number;   // true
'str'.constructor === String; // true
true.constructor === Boolean; // true

However, since this method accesses a property instead of using an operator, it is susceptible to a few problems. For example, it’s quite easy to change the value of the property. We will also throw an error by accessing a property on null or undefined. It’s easy to check equivalence for null and undefined, but there isn’t a good way to check a changed property value.

var Custom = function(){};
var custom = new Custom();

custom.constructor = null;
custom.constructor === Custom; // false

custom = null;
custom.constructor === Custom; // TypeError: Cannot read property 'constructor' of null
custom === null;               // true

Object.prototype.toString method

Despite the wordiness of this approach, it avoids many problems of the above methods. Object.prototype.toString4 returns a string in the form "[object Type]", which can be compared similarly to typeof. We must call it the long way, instead of simply toString, because the method may be overridden along the prototype chain. Unfortunately, we must compare with strings, and custom objects will all return "[object Object]".

[1,2,3].toString();                        // "1,2,3"
Object.prototype.toString.call([1,2,3]);   // "[object Array]"
Object.prototype.toString.call(new Date())
  .slice(8, -1) === 'Date';                // true
Object.prototype.toString.call(
  new (function Custom(){}));              // "[object Object]"

As with constructor, this method wraps primitive types in their object equivalent. As a bonus, it will return successfully when passed in null or undefined.

Object.prototype.toString.call('str');     // "[object String]"
Object.prototype.toString.call(10);        // "[object Number]"
Object.prototype.toString.call(false);     // "[object Boolean]"
Object.prototype.toString.call(null);      // "[object Null]"
Object.prototype.toString.call(undefined); // "[object Undefined]"

Conclusions

I would be presumptuous to claim there is one best way to check types in JavaScript. In truth, there are many valid ways, each with their pros and cons, and each better suited for specific situations and projects. Having a better understanding of these options gives us power to make an informed decision that’s best for our code. For convenience, here is a table summarizing a few properties of each approach.

  typeof instanceof constructor toString
avoids string comparison    
commonly used    
checks custom classes    
directly check null      
directly check undefined    
works across windows5    

  1. Additional approaches include duck testing (assuming type based on characteristics), specific methods like Array.isArray or Number.isNaN, and other comparative methods such as Object.prototype.isPrototypeOf. There are probably others I forget here.

  2. ECMAScript: A general purpose, cross-platform programming language, §11.4.3, pp. 39–40.

  3. ECMAScript 6 provides the additional type symbol. The specification also introduces additional type checking methods, such as Number.isInteger.

  4. A slightly abbreviated version of this is ({}).toString.call. In some browsers, you may also call window.toString.call or toString.call, but results may vary. For example, while writing this post, window.toString.call(null) returns "[object Window]" in my version of Chrome and "[object Null]" in my version Firefox.

  5. Every window/frame has unique instances for each built-in object. If we compare Array in one window with Array from another, they will differ. This happens rare enough in my experience that I didn’t include any examples. Read more about it in this post by Douglas Crockford.