JavaScript Types & Coercion Corner Cases

Bola Adebesin - Feb 16 - - Dev Community

Recently, I've been learning about in-depth JavaScript concepts. This has exposed me to programmer and author Kyle Simpson of the "You Don't Know JS" series.

I've heard it mentioned that JavaScript is a weird language and I've heard praise and criticism alike for it's loosely typed approach. As I've been reading parts of the EcmaScript Specification Document and learning from Kyle Simpson's videos, I have a greater appreciation for what folks mean.

Some fun examples:

-0 === 0 // true 

var workshopStudents = []; 
if(workshopStudents) // true 
if(workshopStudents == true) // false 
Enter fullscreen mode Exit fullscreen mode

The triple equals ignoring the negative zero was an edge case I'd never been exposed to before.

But the second example, was an even more surprising scenario. If you're familiar with the concept of falsy and truthy in JavaScript, then you know that there is a list of values that JavaScript interprets as "falsy":

  • undefined
  • null
  • ""
  • 0
  • -0
  • 0n
  • NaN

Missing from that list is an empty array. So it makes sense that the conditional if(workshopStudents) evaluates to true, but is surprising that if(workshopStudents == true) evaluates to false. I learned that this happens because of how the double equals works in JavaScript under the hood.

Essentially, in the first conditional, workshopStudents is converted to a boolean type and because it is not on the falsy list it's boolean coercion evaluates to true.

In the second conditional workshopStudents, is not coerced into a boolean type first. So the two conditionals use two different evaluation algorithms. In the double equals algorithmic evaluation,workshopStudents is coerced into its primitive type before it is compared to the boolean value true. Well, it turns out the primitive value of an empty array is an empty string, "". And an empty string is on the falsy list.

Because one conditional uses the double equals and one does not they operate differently behind the scenes and trigger two different abstract operations. This leads to two outcomes that appear to contradict one another on the surface. I found this to be fascinating and kind of complex. And this is just one example of many.

I have no doubt in my mind that it's important to become familiar with these kinds of scenarios in JavaScript. I've been coding in JavaScript long enough to know when I receive an array of data from the backend I need to write if(arr.length){...} and NOT if(arr){...} if I want to avoid trying to access nonexistent data.

But I'm curious about how people typically deal with the different coercion corner cases in everyday code. After going through some exercises, I found that trying to cover all of the corner cases led to A LOT of code. I tried to make the code clear using notes referencing the corner cases, but I couldn't help but think, "This is very tedious".

One suggestion from Kyle Simpson, is to reduce polymorphic functions. So, writing a function that only accepts number and string types rather than a function that accepts numbers, string, boolean, and object types. He also recommends knowing what types you are dealing with ahead of time (e.g. before you compare two variables you should know the type of each variable and consider what corner cases may arise from the comparison).

I imagine this is also one of the reasons that TypeScript has become so popular in recent years.

. . . . . . . . . . . .