After you read this article you will:

Understand how to manage types in JavaScript with sophistication.

in JavaScript with sophistication. Never display NaN , Null , or undefined to the user ever again.

, , or to the user ever again. Learn how to type-check various data types including integers and floats.

Beside the fundamentals, here is “generally” everything you need to know about JavaScript types to build reliable business critical applications in the real world.

Si vis pacem, para bellum

** Prerequisites for this Article **

Optionally destructure to make type-checking more readable. i.e.

is(“yo”, “yo”) === Object.is(“yo”, “yo”)

Copy and paste this into your Dev Tools before you play:

The Most Common Cause Of Type Errors

tl;dr Avoid implicit coercion for if and ternary statements; always compare.

Relying on implicit coercion for conditions can cause time consuming repercussions. Because it works on most occasions it requires discipline to rewire you brain to avoid it. Doing so will prevent huge amounts of wasted debugging time in the future.

Never do this

if(value) | Problems: 0, null, “”, undefined, coerced values, NaN === false. As you can tell, this is a dangerous shortcut for non-booleans.

| Problems: 0, null, “”, undefined, coerced values, NaN === false. As you can tell, this is a dangerous shortcut for non-booleans. if(typeof value === “object”) | Null is an object, Array is an object

| Null is an object, Array is an object if(expectedNumberValue) | Once again 0 and NaN will coerce to false

| Once again 0 and NaN will coerce to false if(typeof expectedNumber === “number”) | NaN is a number

| NaN is a number if(isNaN(value)) | Avoid as isNaN performs unfamiliar coercions

Always Do this

Explicit Boolean condition if(value === true) | if(value === false)

| Explicit function condition if(typeof value === “function”)

Explicit empty condition if(value === “”)

Explicit zero condition if(value === 0)

Explicit null condition if(value === null)

Explicit undefined condition if(value === undefined)

Explicit NaN condition if(is(parseFloat(value), NaN))

Yes it is safe and recommended to use null , undefined and NAN

, and Use === over ==. == is NOT faster, === is explicit, == is implicit

One exception

For expressions that are indefinitely booleans and within the same scope as the condition yes below is still explicit type-checking.

Conclusion: BE EXPLICIT with all your conditional logic.

Prevention + Cure: When “Must” I Enforce Checks?

Prevention

Near the beginning of what you regard to be an abstraction layer or concern that is receiving data. You are not obliged to type-check every function within that abstraction but at least the entry point.

Cure

At the return statement of the final stage of an abstraction layer or concern that is specifically providing data to another layer, server or the user, etc. There is not need to type-check every return statement within that abstraction.

In between

Use your discretion to determine when specific type checks are required, but if you find yourself type-checking everything it’s clearly because you don’t fully understand what’s going on in the bigger picture of your application. Learning about common data types and getting familiar with coercion can help.

Pure functions

Avoid side-effects at all cost. Use Object.freeze, Object.seal and const where feasible.

Yes prevention is more desirable than cure. But cure gives you the option to safeguard and choose alternate endings. Aim to do both.

Type Checkers

You will rarely need the majority of “custom helpers” listed below. The most important aspect to managing JS dynamic types is to conditionally BE EXPLICIT as repetitively mentioned.

Array

isArray(value)

Boolean

typeof value

Date

`${value}` === `${new Date(value.valueOf())}`

Error

value.toString().includes(“Error”) `${value}`.includes(“Error”)

#2 is not calling the internal Object.prototype.toString method, instead it calls the value’s .toString method if the value happens to be an Error.

Function

typeof value

Null

value === null is(value, null)

As commonly known, typeof null === “object”

Number: Including NaN

typeof value

Number: Implicit NaN

!is(parseFloat(value), NaN)

The value will be coerced to a float number or NaN. This is generally more practical than Explicit NaN type-checking. A string value starting with “0x????” will be coerced to a number as expected.

Number: Explicit NaN

is(value, NaN)

There is no coercion, it is directly checking if the value is equal to NaN. This is more consistent than isNaN. e.g isNaN({}) // true, is({},NaN) // false. isNaN performs inconsistent coercions, and NaN doesn’t work with comparison operators (===) so always use Object.is for direct no BS NaN comparisons.

Number: Integer

isInteger(value)

Values represented in a base other than 10 are arithmetically treated like base 10 at run time, and processed with significance since integers are essentially floats.

Number: Decimal

(Number(value) + “”).includes(“.”)

This is NOT checking if a number is a float because all JavaScript numbers are floats (hence why isFloat() parses any and every number). So we check if a number is a decimal because it’s far more useful in real world applications. The value is coerced to a Number, and then coerced to a String. This ensures the decimal is from a final number value.

Object

typeof value === “object” && !isArray(value) && !is(value,null)

Experienced developers “generally” avoid using the prototype chain or .constructor for checking object types as they can easily be re-assigned.

RegExp

is(RegExp(value), value)

String

typeof value

Symbol

typeof value

Undefined

undefined === undefined is(value, undefined)

Undefined is immutable in ES6 and in ES5 with “use strict” it’s safe to use.

Here’s all the non-builtin helpers listed above:

Not too scary looking right, and easy to dissect too (On the house)

isObject and isANaN will likely be the most commonly used in a typical application. Being EXPLICIT with primitive types will vastly reduce the need of any of these functions.