Practical type coercion / conversion and type checks in JavaScript

One of the most discussed topics of JS is the types system in context with the equality operator and explicit and implicit type coercion.

Soo Yes! JavaScript has a lot of weird corner cases when it comes to type equality and coercion.

Some examples

    *NaN === NaN // false
    null == 0 // false
    -0 === 0 // true
    [‘a’] == ‘a’ // true*

There is a huge open source list of examples which you can find here. In reality, most programming languages have some corner cases and many of them even make sense.

What I found interesting about type coercion are not all these corner cases instead I like some practical concepts and learnings which you can take from those corner case examples and introduce the learnings into your codebase.

  • It is really helpful to learn how type coercion in JS works to better reason about your code
  • There are some practical things you can improve in your programs if you know how coercion works
  • You don’t need to know all of the corner cases look up the JS spec if you don't understand a particular check

The first thing you should make sure is to lower the surface area of your functions to keep them as small as possible e.g. avoid overloading your functions.

That means if it doesn't make sense to accept a string and a number as a function parameter don’t try to implement both definitions instead make it visible to the reader of your code that the function only works with numbers.

Keep in mind with more information you usually end up with a better result. This is how diversity works if you have more different opinions and perspectives you create better outcomes. So the more you know about coercion the more you can clean up your code. The first example are undefined and null checks.

// 1.
// You can avoid checking for undefined and null by using the == operator instead
// of === so here you can actually make use of the implicit equality operator conversion
if (a !== undefined && a !== null) {
}

// checks for both undefined and null
if (a != null) {
}

// 2. If you want to check whether an array is an array use the built in method
Array.isArray(MyArray)

// 3.
// Use Number.isNaN(value) to check for NaN
Number.isNan(value)

// 4.
// Do not check boolean with == for boolean or when the type is unknown
// Use truthy correctly
// This is ok if you check existency not boolean

if ({}) {
  // run the code
}

// bad since e.g.
if (object == true) {
  // doesnt work
}

// If you have initialised an empty array or an empty object and you want check for existence
// this will not work since == and === operator implicit coerce
;[] == true // false
Object === true // false

You can just check if the variable has a truthy value or not. That means do this

if (value) {
}

will evaluate to true if value is not:

  • null
  • undefined
  • NaN
  • empty string ("")
  • 0
  • false

You may have already seen some deeper nested objects checks for example if you have a nested object like this

let date = {date: { year: { month: { day }}

If you safely want to know whether date has a day property defined you need to do something like the code below

const date = date && date.year && date.year.month && date.year.month.day

If you have a deeply nested object you will end up writing a lot of the above code. There are a lot of libraries and helpers and also a huge discussion about this topic on stack overflow.

Until recently there were only some packages out there which tried to help you with such cases but recently optional chaining has come into the JS spec.

💪 Optional chaining to the rescue

Optional chaining has finally reached stage 4 of the TC39 process and hopefully, most browsers will support it in the near time

With optional, chaining you can use the ? to check for existence.

const day = date?.year?.month?.day

But until it will officially be merged into the browsers I will end up using idx package which was created by a Facebook team. The best part about it is that it doesn't will ship extra code to your JS bundle since you can remove idx during the build with babel.

import idx from 'idx'
// let date = {date: { year: { month: { day }}
// you can access the nested day property like this
const day = idx(date, (_) => _.year.month.day)

Finally, a program is much more reasonable when you know the types. It is then much easier to use the appropriate built-in language tools to check for the corner cases.