Type checking in JavaScript can often be a pain, especially for new JS developers. I want to show you how to reliably check types in JS and understand them a little more. This post digs through Objects, Primitives, shadow objects/coercion, the typeof operator and how we can reliably get a “real” JavaScript type.

Objects versus Primitives

“Everything in JavaScript is an Object”. Remember it, then forget it. It’s not true. JavaScript makes the subject very difficult to understand though - it presents everything as some form of “object” if we dive into their Prototypes (later). For now, let’s look at types.

To understand JavaScript types, we need a top level view of them:

Number

String

Boolean

Object

Null

Undefined

We have Number , String , Boolean - these are Primitives (not Objects!). This means their values are unable to be changed because they are merely values, they have no properties. The Primitive types are wrapped by their Object counterparts when called, JavaScript will dive between the Number/String/Boolean to an Object when needed (coercion). Underneath, it will infact construct an Object, use it, then return the result (all the instance will be shipped out for garbage collection).

For example using 'someString'.trim(); will spin up an Object underneath and call the .trim() method on it.

Null and undefined are weird (both Primitives too), and distinguish between no value or an unknown value ( null is unknown value, undefined is totally not known or even declared). There is also an Error object.

Object’s however are a different story. You’ll notice I’ve not mentioned Array or RegExp , these are types of Object, let’s investigate. Under the Object tree we have:

Object Function Array Date RegExp



Having broken it down, things seem a little simpler, we have Objects versus Primitives. That’s it, right? No, JavaScript decided it wanted to complicate everything you’d assume logical from above.

⚡️ FREE eBook: 🔥 ForEach, Map, Filter, Reduce, Some, Every, Find “This book is straight to the point, syntax exploration, comprehensive guide, real-world examples, tips and tricks - it covers all you need Todd Motto, author of Exploring JavaScript Array Methods

Typeof operator

From MDN: “The typeof operator returns a string indicating the type of the unevaluated operand”.

Based on our newly acquired knowledge from the above, you wouldn’t expect this to happen:

typeof []; // object typeof {}; // object typeof '' ; // string typeof new Date () // object typeof 1 ; // number typeof function () {}; // function typeof /test/i ; // object typeof true ; // boolean typeof null ; // object typeof undefined ; // undefined

Whyyyyyy?! Function is an Object, but tells us it’s a function , Array is an Object and says it is. null is an Object, and so is our RegExp . What happened?

The typeof operator is a bit strange. Unless you know how to really use it, simply avoid it to avoid headaches. We wouldn’t want something like this to happen:

// EXPECTATION var person = { getName : function () { return ' Todd ' ; }; }; if ( typeof person === ' object ' ) { person . getName (); } // THIS GETS LET THROUGH... // because I stupidly refactored some code changing the names // but the `if` still lets through `person` var person = []; var myPerson = { getName : function () { return ' Todd ' ; } }; if ( typeof person === ' object ' ) { person . getName (); // Uncaught TypeError: undefined is not a function }

typeof let us down here, what we really wanted to know was that person was a plain Object.

True Object types

There’s a really simple way, though to look at it looks like a hack:

Object . prototype . toString . call ();

The .toString() method is accessed using Object.prototype because every object descending from Object prototypically inherits it. By default, we get [object Object] when calling {}.toString() (an Object ).

We can use .call() to change the this context (as it converts its argument to a value of type) and, for example, if we use .call(/test/i) (a Regular Expression) then [object Object] becomes [object RegExp] .

Which means if we run our test again using all JS types:

Object . prototype . toString . call ([]); // [object Array] Object . prototype . toString . call ({}); // [object Object] Object . prototype . toString . call ( '' ); // [object String] Object . prototype . toString . call ( new Date ()); // [object Date] Object . prototype . toString . call ( 1 ); // [object Number] Object . prototype . toString . call ( function () {}); // [object Function] Object . prototype . toString . call ( /test/i ); // [object RegExp] Object . prototype . toString . call ( true ); // [object Boolean] Object . prototype . toString . call ( null ); // [object Null] Object . prototype . toString . call (); // [object Undefined]

We can then push this into a function and more reliably validate our previous function:

var getType = function ( elem ) { return Object . prototype . toString . call ( elem ); }; if ( getType ( person ) === ' [object Object] ' ) { person . getName (); }

To keep things DRY and save writing === '[object Object]' or whatever out each time, we can create methods to simply reference. I’ve used .slice(8, -1); inside the getType function to remove the unnecessary [object and ] parts of the String:

var getType = function ( elem ) { return Object . prototype . toString . call ( elem ). slice ( 8 , - 1 ); }; var isObject = function ( elem ) { return getType ( elem ) === ' Object ' ; }; if ( isObject ( person )) { person . getName (); }

Snazzy.

I put together all the above methods into a micro-library called Axis.js which you can use:

axis . isArray ([]); // true axis . isObject ({}); // true axis . isString ( '' ); // true axis . isDate ( new Date ()); // true axis . isRegExp ( /test/i ); // true axis . isFunction ( function () {}); // true axis . isBoolean ( true ); // true axis . isNumber ( 1 ); // true axis . isNull ( null ); // true axis . isUndefined (); // true

The code powering that does some cool stuff for those interested:

/*! axis v1.1.0 | (c) 2014 @toddmotto | github.com/toddmotto/axis */ ( function ( root , factory ) { if ( typeof define === ' function ' && define . amd ) { define ( factory ); } else if ( typeof exports === ' object ' ) { module . exports = factory ; } else { root . axis = factory (); } })( this , function () { ' use strict ' ; var exports = {}; var types = ' Array Object String Date RegExp Function Boolean Number Null Undefined ' . split ( ' ' ); var type = function () { return Object . prototype . toString . call ( this ). slice ( 8 , - 1 ); }; for ( var i = types . length ; i -- ;) { exports [ ' is ' + types [ i ]] = ( function ( self ) { return function ( elem ) { return type . call ( elem ) === self ; }; })( types [ i ]); } return exports ; });

Thank you for reading!