Javascript Coercions (Conversions)
Tags: javascript, webdev, bug, conversion
Published: 2021-01-06 18:10:45

This blog is about Javascript Coercion - converting one type to another. This blog post covers the hidden steps/algorithms that the Javascript engine takes to convert to another type.

## Motivation
The motivation behind this blog is that many developers have no idea how Coercions or conversion works in Javascript. Therefore, they consider these value conversions something evil but it’s actually not. Many developers I talked to, think that these conversions have bugs. I don’t think you can call them bugs but inconsistencies.

## Introduction
Let's start with a few weird cases of conversions.

```js
[] == 0     // true
[] == ![]   // true, WHY?
NaN == NaN  // false, weird
1 < 2 < 3   // true, cool
3 > 2 > 1   // false, wait what?
```
For some of you, some of these examples might look okay to e.g. `[] == 0` or `NaN == NaN` but the other might looks weird. Once you know the algorithm that the javascript engine uses to convert these types, this will look normal.

## Abstract Operations
There are certain sets of operations known as **Abstract Operations** that help in converting values from one type to another. 
Now keep in mind that these operations are not actually available in Javascript, you can't call them just like a normal function. They are only called by Javascript Engine.

### ToPrimitive
This operation converts any non Primitive value to a primitive value i.e. either to a number or to a string, depending on a hint, that is passed to this method `toPrimitive(object, hint)`. For example, if there is some string-based operation on a non-primitive value, it will send String as a hint.

This method accepts two arguments `(object, hint)`. The first one is the non-primitive value that needs to be converted. The second one the hint. The hint is either `string` or `number`.
There are two more abstract operations, one of them is called depending on the hint. Those operations are 
* `valueof()`
* `toString()`

If the hint is `number`, `toPrimitive` will call `valueof` operation, which will try to convert the object to a number, in case if it fails, it will go for `toString`. 

If the hint is `string`, `toPrimitive` will call `toString` operation, which will try to convert the object to a `string`, in case if it fails, it will go for `valueOf` 
 
## Converting to String
Starting with strings, let's take a look at some easy examples of converting to Strings.

```js
undefined == "undefined"
null == "null"
false == "false"
42 == "42"
0 == "0"
NaN == "NaN"
-0 == "0"   // Edge Case
```

All the primitive types, when converted to a string, are just wrapped with double-quotes. `-0` is a special case, which is converted to 0. 
(💡 Yes, -0 actually exist in JavaScript )

Now let's take a look at some non-primitive to primitive (string examples)
```js
[1, 2, 3] == "1,2,3"
[,,,] == ",,,"
[null, undefined] == ","
[] == ""
[[],[],[]] == ",,"
```
Some complex examples, may or may not looks normal to you (depending on your experience) but don't worry we will talk about the actual algorithm in just a while.

* An array with primitive values, when converted to a string, is that same array, joined together with commas.
* An array with an empty index is converted to a combination of commas.
* `null` or `undefined` in an array is converted to an empty string ( `[1, null, 2]` will be converted to `1,,2` )
* An Empty Array always becomes an empty string.
* An empty nested array also becomes an empty string.

Some more examples
```js
{ } == "[object Object]"   // Empty Object
{ a: 2 } == "[object Object]"
function() { } == "function(){}"
```
An object (either empty or not) when converted to String, it is `[object Object]`. Functions, when converted to a string, just wraps themselves in double-quotes.

Okay, now let's take a look at the **algorithm** that the Javascript engine uses to convert a value to a string type.

![to string](https://i.imgur.com/QBzCNqj.png)



So the algorithm is 
* undefined will be `"undefined"`
* null will be "null"
* boolean will be "true" or "false"
* Number when passed, will be wrapped in double-quotes.
* So on...

The Object will use the `toPrimitive` abstract operation with hint `string`. The returning value will be then again passed to this `toString` and it will return you the result.

## Converting to Number

```js
undefined == NaN
null == 0
True == 1
False == 0
"0" == 0
"-0" == 0
"" == 0
```

Some weird cases are `undefined` is `NaN` but `null` is 0, "-0" is -0 but -0 is "-0" (previous example, converting to string). Well, these are just inconsistencies. 
Take a look at a few more non-primitive examples.

```js
[""] == 0
[[[]]] == 0
[null] == 0
[undefined] == 0
[1,2] == NaN
```

Almost all of them converts to 0, except for the last example.
To understanding the working, keep in mind two rules of Javascript
* An empty string, when converted to a number will always be 0.
* An empty array when converted to a string, will always be an empty string.

Now what happens here
* `[""]` is converted to empty string ( `""` ), which is then converted to 0.
* `[[[]]]` nested empty array is converted to an empty string, which is then converted to 0.
* `[undefined]` and `[null]` is converted to an empty string, which is then converted to 0. (Null and Undefined always becomes an empty string. `[undefined, null]` when converted, becomes `","` )
* Last one is `NaN` because `[1,2]` is when converted, it becomes `"1,2"` which is then converted to a number ( `NaN`, because of the comma )

Here's the algorithm that the JavaScript engine uses to convert any type to a Number.
![to number](https://i.imgur.com/rcn0Rw9.png)
The algorithm for converting an object to a Number is the same as converting any object to a string with the difference of **hint**, which will be **Number** in this case.

## Converting to Boolean

```js
// Falsey                         Truthy
0, -0                       // Everything Else
""
false
undefined
null
NaN
```

Booleans are easy. All the values that are mentioned in the list of `Falsey` are false when you convert them to boolean and everything else (an object, a non-empty string, numbers greater than 1, etc) will be true when converted to boolean. These values will always behave the same under any circumstances. Just memorize this list and you'll be able to write bugs free code when converting to a boolean.

Here's what the docs say:
![to boolean](https://i.imgur.com/IBi5jCy.png)
Pretty straight forward, isn't it?

# Coercions
## Double Equals (==) - Good or Bad?
I'm sure you've seen a lot of blog posts and articles where the author discouraged you not to use double equals. These blogs author wants you to always use triple equals `===`. The reason they give is that `==` do the coercion which is something evil.
Well, I disagree with this. Coercion is evil when you don't know anything about it and that's why you end up having buggy code (which is not actually buggy). Instead of avoiding `==`, whenever possible, you must familiarize yourself more with the arguments and values type. 
Now I do not say to always use `==` and never use `===` and I also disagree with what those blog articles suggest to you.
Use a suitable one based on the scenario. You actually can not ignore `==` at all. In fact, you are already using it in your code but you don't know. **We all do coercions, but we don't know that**.

## Implicit Coercion
```js
let arr = [`1,2,3,4];
while (arr.length) {
 arr.pop();
}
```

The above code snippet will execute until the length of the array is 0. Here we've used implicit coercion (the **double equals**). HOW? 
So we have an array `arr` and we get its length by `arr.length` which returns 4. Notice we used `arr.length` as a condition of `while(){}` which is actually converting the number to a boolean. Now as you studied earlier, any number greater than 0 is true, when converted to boolean, so this returns true until the length becomes 0.

Another example:
```js
var userAge = document.querySelector(".ageInput");

function doubleAge(age) {
   return age * age;
}

doubleAge(userAge.nodeValue);
```
Here again, we did implicit coercion(the **double equals**). The `userAge` is getting a value from the HTML input element, so it's of type string, but the `return age * age` is actually doing a multiplication, here the age is converted to number for multiplication.

One more:
```js
var userAge = 21;
console.log(`Your age is ${userAge}`);
```
Here the type of `userAge` is an integer but when passed as an argument in `console.log` it is implicitly converted to a string.

## Conclusion
On taking a look at the specs, we can conclude that 
- JavaScript has some edge cases, which can be avoided by reading the documentation
- It's better to understand your variable types rather than using triple equals (===) everywhere
- We are using double equals (==) in our code unintentionally