DEV Community

Hugo Di Francesco
Hugo Di Francesco

Posted on • Originally published at codewithhugo.com on

JavaScript Data Type Check Cheatsheet

JavaScript (JS) is a lightweight, interpreted, or just-in-time compiled programming language with first-class functions.

MDN Web Docs - JavaScript

Detailed in this post are common JavaScript data type checks, pitfalls and idiomatic workarounds.

Classic JavaScript Data Type Checks

Here’s a collection of the most common data type checks in JavaScript. Whether you want to check if a variable contains a Date, a Promise, a plain old JavaScript object or an Array, it’s all here.

Everything from primitive types like number, boolean, string to detecting functions.

Check if JavaScript variable contains an object

typeof does output 'object' for objects.

It also does so for null and Arrays.

const object = {};

console.log(typeof object); // 'object'
console.log(typeof null); // 'object'
console.log(typeof []); // 'object'

console.log(object instanceof Object); // true
console.log(null instanceof Object); // false
console.log([] instanceof Object); // true

What’s more, much like in the case of Arrays, if there is inter-frame communication, it tends to share objects and arrays (see JavaScript array type check - “is array” vs object in-depth). Therefore, checking whether something is a simple object vs an instance of a class is difficult.

In JavaScript you’ll notice that everything is an Object, and when you try to access a property that doesn’t exist, it’ll fail quietly (ie. return undefined):

console.log('aaaaa'.noProperty); // undefined
console.log([].nothing); // undefined

In idiomatic JavaScript code, we leverage this property to be just defensive enough, for example if we expect an object that has a growl method, but something else might be passed in:

function safeGrowl(anything) {
  if (anything.growl) {
    anything.growl()
  }
}

safeGrowl('hello'); // nothing
safeGrowl({ growl() { console.log('Growl!') }}); // Growl!

The moral of the story is: don’t check that something is an object, check that it has the properties you need (that’s what’s called duck typing).

Check if a value is a string in JavaScript

For strings, we can use a typeof check.

Much the same as for object checks, JavaScript won’t fail loudly when you try to use something as a string that isn’t a string, it will tend to just coerce it or call .toString on it.

const string = 'Hello World';
console.log(typeof string); // 'string'

// Implicit coercion to string using templates
const helloCount = 2;
const newString = `Hello number ${helloCount} at ${new Date('2019-06-23T21:00:26.861Z')}`;

console.log(newString);
// 'Hello number 2 at Sun Jun 23 2019 22:00:26 GMT+0100 (British Summer Time)'

This works with dates, number. For arrays and other objects that don’t directly implement a toString method, I would suggest using JSON.stringify.

const myArray = ['a', 'b', 'c'];
const mySimpleObject = { key: 'value' };

console.log(`${myArray} ${mySimpleObject}`); // 'a,b,c [object Object]'
console.log(`${JSON.stringify(myArray)} ${JSON.stringify(mySimpleObject)}`)
// '["a","b","c"] {"key":"value"}'

Check if a value is a JavaScript Number/Integer

JavaScript Numbers are a bag of fun. They’ve got a similar gotcha to object checks, that’s the NaN (Not a Number) value. NaN usually is the output of trying to do arithmetic where one of the operands is not a Number.

The quirks of NaN is that it’s not equal to itself, and it’s actually a Number, just like Infinity and - Infinity

console.log(NaN == NaN); // false
console.log(NaN === NaN); // false

console.log(typeof NaN); // 'number'
console.log(typeof Infinity); // 'number'
console.log(typeof -Infinity); // 'number'
console.log(typeof 123); // 'number'

Checking that a Number is not NaN (Not A Number)

One NaN check would just be:

const a = NaN;

function isNotANumber(maybeNotANumber) {
  return maybeNotANumber === maybeNotANumber;
}

isNotANumber(a); // true

The recommended approach is the following, there’s a built-in Number.isNaN function:

console.log(Number.isNaN(NaN)); // true
console.log(Number.isNaN('abc')); // false
console.log(Number.isNaN(1234)); // false
console.log(Number.isNaN(123.11)); // false
console.log(Number.isNaN(true)); // false

The difference between Number.isNaN and the isNaN global is that Number.isNaN checks that the value passed in is a Number and it is NaN.

The older global isNaN function just goes for the literal check that something is not a number.

console.log(isNaN(NaN)); // true
console.log(isNaN('abc')); // true
console.log(isNaN(1234)); // false
console.log(isNaN(123.11)); // false
console.log(isNaN(true)); // false

Check if a JavaScript variable contains an Integer

To check that JavaScript variable (or value) is an integer, we can use Number.isInteger:

console.log(Number.isInteger(123)); // true
console.log(Number.isInteger(12.3)); // false
console.log(Number.isInteger(123.0)); // true

Check if a JavaScript variable contains a useable Number value

To check that we’ve got a useable input value, we should check that the type is number and that the value is not NaN:

function isValidNumber(maybeNumber) {
  return typeof maybeNumber === 'number' && !Number.isNaN(maybeNumber);
}

console.log(isValidNumber('aaaaa')); // false
console.log(isValidNumber(NaN)); // false
console.log(isValidNumber(123)); // true
console.log(isValidNumber(1.23)); // true

Check if a value is a boolean

As with JavaScript string and number data types, in JavaScript the pattern is to assume something is a boolean (or cast it to boolean) rather than checking that it’s a boolean. That’s because in JavaScript we can use logical operators with non boolean values due to the loose typing, this is usually explained through the concept of “truthiness” and “falsiness”.

When JavaScript is expecting a boolean and it is given one of the values below, it will always evaluate to “falsy”

MDN Web Docs - Falsy

The values in question (falsy values) are: false, 0, '' (or other empty string), null and undefined. Any other value will be evaluated to true.

There are some cases where false means something other than undefined, in that case, it’s possible to check that a value is falsy and a boolean by using the typeof:

console.log(typeof true); // 'boolean'
console.log(typeof false); // 'boolean'

Check if a variable contains an Array

To check if a JavaScript variable is an Array, there’s a built-in Array.isArray.

The fun gotcha with JavaScript Arrays is that they’re just objects.

console.log(([]) instanceof Object); // true
console.log(typeof []); // 'object'

A way to duck-type an Array is to use existence of a .length property. However this can be pretty weak since there’s nothing enforcing that Arrays should be the only type to have a .length property. The pattern tends to look like so:

function processList(maybeArray) {
  if (!maybeArray.length) {
    return []
  }
  return maybeArray.map(i => i); // literally copy
}

Now this code doesn’t actually check that maybeArray is an Array. It sort of does that but in the same line of code ie. !maybeArray.length, it also states that maybeArray must have a non-falsy length, ie. in the case where it is in fact an Array, it shoul also not have length 0 (must not be empty).

It’s trivial to trick the above and make it crash on .map with for example using the following data: { length: 'aaaa' }. That’s not the point, if consumers of this function are trusted, this kind of check can be fine.

Using Array.isArray however works as follows:

console.log(Array.isArray({})); // false
console.log(Array.isArray(new Map())); // false
console.log(Array.isArray(new Set())); // false

console.log(Array.isArray([])); // true
console.log(Array.isArray(new Array())); // true

For a closer look at the internals of how we check for Arrays, see JavaScript array type check - “is array” vs object in-depth. The big gotcha with built-in JavaScript data types like Array, Object and Date in JavaScript is that communicating between frames means the constructors and therefore instanceof checks don’t work.

Check if an object is an instance of a specific class/constructor function

Say you’ve got a set variable and you want to check that it’s a React Component, you can do:

import React, { Component } from 'react';

const myComp = new Component();

function isReactComponent(maybeComponent) {
  return maybeComponent instanceof Component;
}

isReactComponent(myComp);
// true

isReactComponent({});
// false

This also works with constructor functions:

function Dog (name) {
  this.name = name
}

const max = new Dog('Max');

console.log(max instanceof Dog); // true

Another interesting thing is it works all the way up the prototype chain/class hierarchy:

console.log(({}) instanceof Object); // true
console.log((new Dog) instanceof Object); // true

Check if an object is an Error

Error is just a constructor/class. So in the same way we could check for React.Component or Dog class:

function isError(maybeError) {
  return maybeError instanceof Error;
}

isError(new Error('Something went wrong')); // true
isError(new EvalError()); // true
isError(new InternalError()); // true
isError(new RangeError()); // true
isError(new ReferenceError()); // true
isError(new SyntaxError()); // true
isError(new TypeError()); // true
isError(new URIError()); // true

See more about Fundamental Objects on MDN.

Check for a valid JavaScript Date string (parseable date-string)

function isValidDateString(maybeDateString) {
  return !Number.isNaN(Number(new Date(maybeDateString)));
}

console.log(isValidDateString('abcd')); // false
console.log(isValidDateString(1234)); // true
console.log(isValidDateString('2019-06-23T22:00:26.861Z')); // true

The above function doesn’t actually check whether something is a valid string but whether it’s convertible to a valid date.

For most intents and purposes, it will catch dodgy date strings, and has the benefit of not being unreadable at the cost of allow number timestamps to be passed in. A more apt name might be isConvertibleToDate. Disallowing numbers would just be a case of adding a typeof maybeDateString === 'string'.

Check for a valid JavaScript Date

To check whether or not something is a valid, we’ll just take the same approach as checking whether it’s convertible to a date

function isValidDateObject(maybeDate) {
  return (
    typeof maybeDate === 'object' &&
    !Number.isNaN(Number(new Date(maybeDate))
  );
}

isValidDateObject('abc'); // false
isValidDateObject(1234); // false
isValidDateObject('2019-06-23T22:00:26.861Z'); // false
isValidDateObject(new Date('2019-06-23T22:00:26.861Z')); // true

You could also apply the instanceof approch:

function isValidDateObject(maybeDate) {
  return maybeDate instanceof Date;
}

isValidDateObject('abc'); // false
isValidDateObject(1234); // false
isValidDateObject('2019-06-23T22:00:26.861Z'); // false
isValidDateObject(new Date('2019-06-23T22:00:26.861Z')); // true

This has some cross-frame issues and you never know when someone might mess with the global Date to replace it with their own custom, non-standard version.

Check if a JavaScript variable is a Promise

Promise checks are done using instanceof with all the usual cross-frame or custom implementation caveats:

console.log({} instanceof Promise); // false

With Promises there’s also the issue for then-ables. Which for most intents and purposes may as well be Promises but which won’t pass our above check.

In async/await-enabled environments, you’ll also note that await-ing a function that doesn’t return a Promise doesn’t cause any unintended side-effects, the return value of the function (even if it’s not an async function), can be stored the same way as you would if you hadn’t await-ed.

Check if a JavaScript variable is a function

As mentioned on the MDN Web Docs JavaScript is a “programming language with first-class functions”. First-class functions, just means functions are treated like any other variable.

console.log(typeof (() => {})); // 'function'
console.log(typeof function () {}); // 'function'
function myFunc () {}
console.log(typeof myFunc); // 'function'

Refer to the object example to see how an idiomatic “run this function if it exists” would look.

Debugging JavaScript Data Type Issues

What does each of the following look like when it’s console.log-ed?

One of the big issues is that console.log tends to stringify whatever object it’s passed using its .toString() method. A lot of the time, a combination of pattern matching, logging out typeof and logging out a JSON.stringify-ed version of the object gives good results.

Figuring out if something is a Promise

Forgot to await a function that returns a Promise (including an async function).

You usually want to do something with the output of the Promise:

A promise that’s supposed to return a list

const fetchList = async () => ['first-item'];

async function doSomething() {
  const [firstItem] = fetchList();
}

doSomething()
// UnhandledPromiseRejectionWarning:
// TypeError: fetchList is not a function or its return value is not iterable

A promise that’s supposed to return an object

const fetchObj = async () => ({ property: 'value' });

async function doSomething() {
  const obj = fetchObj();
  console.log(obj.property);
  console.log(obj);
  console.log('done')
}

doSomething()
// undefined
// Promise {
// { property: 'value' },
// and so on

Debugging Array vs Array-like

There are a few Array-like objects in JavaScript for example arguments, NodeLists (output of document.querySelectorAll).

The first thing to do is to just log them out:

const anchors = document.querySelectorAll('a');
console.log(anchors); // { "0": {}, "1": {} }

function multiVariateFn() {
  console.log(arguments)
}

multiVariateFn(1, 2, 3); // [Arguments] { '0': 1, '1': 2, '2': 3 }

Compare those outputs to:

console.log([1, 2, 3]); // [1, 2, 3]

Here is the old-school method of converting them to regular Array-s (which means you can use Array.forEach/.map etc.):

const anchors = Array.prototype.slice.call(
  document.querySelectorAll('a'),
  0
);

function multiVariateFn() {
  const args = Array.prototype.slice.call(arguments, 0);
  return args.reverse();
}

console.log(multiVariateFn(1, 2, 3, 4)); // [4, 3, 2, 1]
console.log(multiVariateFn('a', 'b', 'c')); // ['c', 'b', 'a']

The ES6 approach would look something closer to this, they take advantage of array spread syntax and rest parameters syntax respectively.

const anchors = [...document.querySelectorAll('a')];

function multiVariateFn(...args) {
  return args.reverse();
}

console.log(multiVariateFn(1, 2, 3, 4)); // [4, 3, 2, 1]
console.log(multiVariateFn('a', 'b', 'c')); // ['c', 'b', 'a']

A more conservative approach could leverage Array.from (see Array from MDN Web Docs):

const anchors = Array.from(document.querySelectorAll('a'));

function multiVariateFn() {
  return Array.from(arguments).reverse();
}

console.log(multiVariateFn(1, 2, 3, 4)); // [4, 3, 2, 1]
console.log(multiVariateFn('a', 'b', 'c')); // ['c', 'b', 'a']

Source Materials - Further Reading

While creating this JavaScript Data Type Check guide, I took inspiration from some of the top relevant posts:

As well as the MDN Web Docs for JavaScript.

unsplash-logo
Daniel Fazio

Top comments (0)