Understanding JavaScript type conversions
Tags: javascript
Published: 2019-08-31 23:20:28

# Introduction 

Probably the most confusing part of JavaScript is how it works with types. A lot of weirdness can be achieved thanks to JavaScript being a flexible and forgiving language with a rich history. You've likely seen [fun things](https://github.com/aemkei/jsfuck) like that:
```javascript
(NaN+Object()["to"+String["name"]]["call"]())[11] // Produces letter "U" 😮
```

The example above is too extravagant, but in general *good* developers should understand all the lowdowns of the programming language they are using.

Let's clear all the misconceptions about how and when Javascript converts types.

# What types are in JavaScript?
Values in JavaScript are one of the next types:

```javascript
// We can use typeof function to get the type of the value

typeof undefined // "undefined"
typeof 42 // "number"
typeof "hello" // "string"
typeof true // "boolean"
typeof { name: 'Jhon' } // "object"
typeof alert // "function"
typeof Symbol("id") // "symbol"

typeof null // "object"
```
This should be pretty self-explanatory if you worked with JavaScript already.

The `null` value of course is not an object though. Its type is `"null"`. Yet for the historical reasons `typeof` function returns `"object"` for the `null` value.

As JavaScript is a language with weak typing so it will try to do implicit conversions between the types when it happens. But, implicit is a dangerous word to use in a JavaScript world!

# What is type conversion?

When operand or a function parameter doesn't have expected type...
```javascript
3 > "3"  // Expects number, given string
3 + true // Expects number, given boolean
alert(3) // Expects string, given number
alert({ name: 'John' }) // Expects string, given object
```
Javascript converts the value to the expected type following specific rules.

Let's examine each of the most possible of them that you can meet in the code:

## String type conversion
String type conversion applies when the given value is expected to be a string. The most basic example is the *alert* function:
```javascript
alert(3)    // 3 becomes "3"
alert(true) // true becomes "true"
alert(null) // null becomes "null"
```
As you can see, string conversion happens as you would expect in an obvious way.

## Number type conversion
Number type conversion can be met in the math expressions and comparisons. Here is where usually a lot of confusion comes from.

```javascript
2 * "3" // 6
6 / "2" // 3
3 - "1" // 2

3 + "3" // "33" 🤬
```

Excuse me? Yes! The `+` operator actually works a bit differently. **If one of the operands is a string, then all other operands are converted to string too** and it works like string concatenation, not like the math expression:
```javascript
// One of the operands is string "2"
// JavaScript will convert every other operand to string too
1 + "2" + true // The result is "12true"
```

In arithmetic expressions, `boolean`, `null` and `undefined` convert to a numbers as following:
```javascript
1 + true      // true becomes 1, result is 2
1 + false     // false becomes 0, result is 1
1 + null     // null becomes 0, result is 1
1 + undefined // undefined becomes NaN, result is NaN
```
That's it. No ✨ magic, only strict rules!

## Boolean type conversion
This type of conversion happens in logical operations. It follows strict rules too yet they are mostly obvious:
* `0`, `NaN`, `undefined`, `null`, `""` are converting to `false`
* everything else, including objects, to `true`
```javascript
if ("hello") // true
if (0)       // false
if ({})      // true
```

## Type conversions for objects
What JavaScript is going to do if it needs to convert an object to string or number? Let's see:
```javascript
parseInt({}) // NaN (converted to number)
alert({})    // "[object Object]" (converted to string)
alert([])    // ""
```
These are default converted values. You rarely would want to convert objects into primitives... Still, if your code needs a more meaningful conversion you would need to know how to set the conversion rules explicitly.

When converting the `object` type (not array), JavaScript tries to find and call three object methods:

1. Call `obj[Symbol.toPrimitive](hint)` – the method with the symbolic key `Symbol.toPrimitive`.

2. Otherwise if type of hint is `"string"` call `obj.toString()` and `obj.valueOf()`, whatever exists.

3. Otherwise if type of hint is `"number"` or `"default"` call `obj.valueOf()` and `obj.toString()`, whatever exists.

The hint is a type of the primitive the object is going to be converted to.

As you can see, you will need to explicitly set `Symbol.toPrimitive` property for your objects in case you need meaningful visualisation of your object.

### Symbol.toPrimitive
Let's create an object and set `Symbol.toPrimitive` property.

```javascript
const obj = {
  name: "Default conversion"
}

const country = {
  name: "Estonia",
  population: 1291170,

  [Symbol.toPrimitive](hint) {
    // For string conversion
    if(hint == "string") {
      return `Country: ${this.name}, population: ${this.population}`
    }
    
    // Otherwise number conversion
    return this.population
  }
}

alert(obj) // "[object Object]"
alert(country) // "Country: Estonia, population: 1291170"
alert(country + 1) // 1291171
```

## Comparison and type conversion
There is two specific comparison rules. 

* **When doing a non-strict comparison Javascript converts operands to numbers if operands have different type**:
```javascript
0 == "0"  // true. String is converting to a number
0 === "0" // false. Strict comparison compares types too!
"0" != "" // true. There isn't type conversion
```

* `null == undefined`! There isn't any type of conversion here and these values have a different types! Yet **in non-strict comparison `undefined` equals `null` and `undefined`** by design:

```javascript
null == undefined // true. God bless JavaScript ❤️
```

## Conclusion
Here we described the main rules and approach in which JavaScript makes types conversions. If you carefully observe all these rules you will find that they are basically obvious in most cases. Anyway, in the real production code I would encourage you to avoid implicit type conversions and weird comparisons:

* For example, values received from user input will be in the string type. Convert them to number explicitly before using it further:
```javascript
// ... imagine we handled user input event
const money = parseInt(event.target.value);
alert(typeof money == "number"); // true. Now we can safely use money as a number
```

* Concatenate string using template literals instead `+` operator:
```javascript
// Can be confusing to read because the result can vary depending on operands types
const result = one + two 

// Explicitly concatenated string
const text = `${one}${two}` 
```

* Use strict comparison for comparing values with different types to avoid implicit conversion to number:
```javascript
const something = 0;
alert(something == false)  // true
alert(something === false) // false
```
---
That's it! I hope you found this little guide helpful and now you can better understand types and implicit conversions in JavaScript.

Now you should be able to decifer the article image:

```javascript
{} + {}    // NaN, because object is converting to NaN
[] + []    // "", because array is converting to ""
0 == "0"   // true, because "0" is converting to 0
0 == ""    // true, because empty string is converting to 0
"" != "0"  // true, because operands are the same type (string) and no conversion happens
```

Happy coding! ❤️