JavaScript WTFs explained - Type coercion
Tags: javascript
Published: 2022-07-28 23:44:37


---
title: JavaScript WTFs explained - Type coercion
published: true
description:
tags: javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lgtuyr17yikr560tmg3f.jpg
series: JavaScript WTFs explained
---


_Cover Photo by <a href="https://unsplash.com/@tonny_tran?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Tony Tran</a> on <a href="https://unsplash.com/?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>_

![A screenshot of the Firefox developer console with several confusing results output - this will be the subject of today's discussion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t1t92oatsx66m68wr543.png)


Source:

{% embed https://www.reddit.com/r/ProgrammerHumor/comments/w8m9em/just_started_learning_javascript_and_have_a_lot/ %}

The few examples to follow look pretty unhinged, fair warning. 

The factor most at fault is **automatic type conversion** due to loose typing, but **operator overload** is also a problem.

---

## The plus `+` operator is overloaded

The + operator in JavaScript has two different functions. As you might expect, it can **add numbers together**, but it can also **concatenate strings**.

> 💡 **Concatenation** is when two (or more) strings are taped together to form a new, combined string.
> 
> For example, concatenating `"hello"` and `"world"` results in `"helloworld"`.
> This is done with the `+` operator in JavaScript, but other languages might use other methods, such as a `concat()` function or a different operator.

When an operator can do multiple different things like this, it is referred to as an **overloaded operator**.

By contrast, if you look at the minus `-` operator, it only does math, and doesn't have a defined operation for strings at all. Minus is *not* overloaded.

---

## Type coercion

JavaScript has **two distinct equality operators**, one that compares content but *not* type (`==`), and one that compares content *and* type (`===`).

```js
const a = "2"; // string
const b = 2; // number

a == b; // "2" == "2"
a === b; // "2" === 2
```

So how does the loose equality operator (`==`) determine if two variables have the "same" content? It **converts** one or both values.

Taking a look at `"2" == 2`, let's go over the process step-by-step.

1. Check if both values are of the same type. In this case, they **aren't**, and need to be converted.
2. Determine the type(s) to convert to. JavaScript's [loose equality algorithm](https://explained.blog/javascript/loose-equality--algorithm/#comparing-string-to-number) determines which values to convert, and to what. In this case, the string is converted to a number.
3. Compare the converted values. In this case, `2` (converted from string) and `2` (original) are equal, so the return value is true.

Another resource to check for this is the [JavaScript Equality Table](https://dorey.github.io/JavaScript-Equality-Table/).


> 💡 Advanced note on equality for arrays and objects
>
> So far, we've looked at strings and numbers for `==` and `===`, but it functions differently for more complicated constructs like arrays and objects. In these cases, *both* operators check that the operands have the same *reference*. For more information, see [MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Equality).


---

## Empty array + empty array

Let's take a look at the first (and easiest to explain), empty array plus empty array.

```js
[]+[]; // evaluates to "" 
```

Here's what's happening:

1. JavaScript sees the `+` operator and thinks about what types are valid for this: strings and numbers. Additionally, the two operands should match each other.
    * Two strings will be concatenated (`"a" + "b"` => `"ab"`)
    * Two numbers will be added numerically (`1 + 2` => `3`).
2. JavaScript looks at what the actual variables used in the operation, and sees if any need to be converted. In this case, both arrays need to be converted, since they aren't strings or numbers. For `+`, both arguments must be converted to "primitive" values (either a string, number, or boolean).
3. JavaScript uses a [specific set of logic](https://tc39.es/ecma262/multipage/ecmascript-language-expressions.html#sec-applystringornumericbinaryoperator) to cast the arguments to the type it wants
    * Arrays are converted to a predetermined primitive type, which is a string 📝. An empty array becomes an empty string, `""`.
4. Two empty strings concatenated together make an empty string - and there's your result!

> 📝 How does an array become a string?
> 
> In JavaScript, you can test this out by using `String([...])` or `([...]).toString()` to observe how different arrays are transformed into strings.
>
> The elements in the array are transformed into strings, and those stringified elements are joined together with a comma.
>
> ```js
> String(["a", "b"]); // "a,b"
> String(["a", 2, ["b", 3]]); // "a,2,b,3"
> ```
>
> This is much different from the result of [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)!

---

## Empty array + empty object

This one works very similarly to the above. JavaScript determines that both operands need to be strings in order to continue, and converts them both. As we have seen previously, `[]` becomes `""`.

The only difference is that an empty object `{}`, when cast to a string, becomes `"[object Object]"`.

> 💡 Why `"[object Object]"`?
>
> From https://stackoverflow.com/a/25419538
> 
> As others have noted, this is the default serialisation of an object. But why is it [object Object] and not just [object]?
>
> That is because there are different types of objects in Javascript!
>
> [Object specification](https://es5.github.io/spec.html#x15-toc)

---

## Empty object + empty array

Despite the title of this section, the `{}` is actually *not interpreted as an object*. 

When used at the start of an expression, and there's no other indication that it *ought* to be an object, JS decides it's a block statement instead.

Here's what a *non*-empty block statement looks like:

```js
{
    let foo = 'bar';
    console.log(foo);
}
```
You've seen these before, though they're usually used with constructs like if-statements, loops, and the like:

```js
if (true) {
    let foo = 'bar';
    console.log(foo);
}
```

In the case of this particular example, it's interpreted as an **empty block statement**, which is functionally equivalent to typing nothing.

The reason the previous example was interpreted as an empty object instead of an empty block was that it was being used as a right-hand operand for `+`. JavaScript supports `+` being used to cast to number type when used without a left-hand operand:

```js
// left and right
1 + "1"; // "11"

// casting with right-side only
+"1"; // 1
+1; // 1
+true; // 1
```

Thus, we can reduce the problem to `+[]`. 

When JS converts an array to a number, it is effectively the same as casting that same array to a string (as seen in the two previous examples), *then* casting that string to a number. (Note that casting the array to a string converts values such as `null` and `undefined` to empty string as well.)

```js
[].toString(); // ""
Number(""); // 0

["1"].toString(); // "1"
Number("1"); // 1

["1", "2"].toString(); // "1,2"
Number("1,2"); // NaN, can't parse this

["a"].toString(); // "a"
Number("a"); // NaN, can't parse this
```

So now we can see where the `0` comes from - it's the numeric value of an empty string.

---

## Type coercion with different operators

So far we've worked only with the `+` operator, which is an *overloaded operator*: it can be used for more than one thing, depending on the operands. However, the mathematical opposite, `-`, is only used for mathematical subtraction, and can't be used to manipulate strings at all.

The examples used in the reddit post:

```js
9 + "1"; // "91"
91 - "1"; // 90
```
Since one of the operands of `9 + "1"` is a string, even if it's a numeric string, the `+` operation results in a string concatenation.

However, since `-` cannot be used for string concatenation, the "1" is interpreted as numeric.

Here are some more examples of this principle in action:

```js
// two numeric operands is always math
1 + 2; // 3
1 - 2; // -1

// numeric strings are concatenated for addition, and converted for subtraction
1 + "2"; // "12"
"1" + 2; // "12"
"1" + "2"; // "12"
1 - "2"; // -1
"1" - 2; // -1
"1" - "2"; // -1

// non-numeric strings are concatenated for addition, and result in a math error for subtraction
1 + "hello"; // "1hello"
"hello" + 1; // "hello1";
1 - "hello"; // NaN
"hello" - 1; // NaN

```

---

## When is a `true` a `1`?

```js
true + true + true === 3; // true
true - true; // 0
```


As in the section dealing with `[]+[]`, we know that JavaScript will convert operands if they aren't of the appropriate type. In this case, the boolean value `true` is cast to a numeric `1`, and as we all know, `1 + 1 + 1` is in fact three.

Similarly, `1 - 1` we know to be zero.

Note that these *can* be cast to the strings "true" and "false", for example when a string is an operand:

```js
1 + "hello" + true + false; // "1hellotruefalse" 
```

---

## When is a `true` *not* a `1`?

```js
true == 1; // true
true === 1; // false
```


The numeric value of `true` is `1`, so the loose equality operator sees them as having the same value.

The reddit post lists the following statements:

```js
true == 1; // yes, because true is cast to 1
true === 1; // no, because true and 1 are not the same type
[] == 0; // yes, because empty array is cast to 0 (see empty object + empty array example)
```

---

## Abusing type coercion for fun and profit

The last example from reddit is basically a gigantic exercise in learning automatic type conversion, so let's break it down.

```js
(!+[]+[]+![]).length; // 9
```

The first thing we need to ask here is: The length of *what*, exactly? Let's take a look at what's in those parentheses.

```js
!+[]+[]+![]; // "truefalse"
```

The `.length` might have led to you believe that it was an *array* of length 9, but here we have a *string* of length 9. Now, let's figure out why we get that string out of that expression.

Let's separate it out a little bit, so it's more readable:

```js
(!+[]) + ([]) + (![]);
```

That first set of parentheses contains two **explicit** conversions, which are worked from the "inside out" - the closest conversion to the value goes first, then it works its way outwards. So first, `[]` is our value, which is converted to a number by `+[]`, which results in `0`. Next, the `!` converts the number to a boolean value and inverts it - `0` converts to a boolean `false`, which when inverted results in `true`.

Let's leave that empty array in the middle for a moment. It isn't being explicitly cast.

On the right, we have `![]` - at first glance, it seems like this also ought to be true, since `!+[]` is true. However, the extra conversion to number makes all the difference! When converted *directly* to a boolean value, an empty array results in `true`, which when inverted is `false`.

```js
Number([]); // 0
Boolean([]); // true
Boolean(0); // false
```

So now, we have the simplified form of our expression from earlier:

```js
true + [] + false
```
Now on to that `+` operand. The choice with `+` is either numeric plus, or string concatenation, and JavaScript's internal ruleset says that when you are using `+` with a boolean and an array, both are cast automatically to strings and concatenated. From left to right, we can work this expression out:

```js
// true + [] + false
true + []; // "true"
"true" + false; // "truefalse"
```
And there you have it.

---

## Wait, so... ***why***?

### Why is JavaScript loosely typed, and why does it do automatic type conversion?

Brandon Eich designed the language in 1995 in [under two weeks](https://thenewstack.io/brendan-eich-on-creating-javascript-in-10-days-and-what-hed-do-differently-today/). I don't really think there's a "why" to it, to be honest. Maybe we can blame a lack of sleep on his part.

### Why does X convert to Y?

There *is* an internal logic to all the automatic type conversion JavaScript does - it is, after all, run by a computer, and computers don't just spontaneously decide to do something a little different to mess with you.

You might notice that arrays and objects are tricky, being cast to many different possible primitive values:

```js
// empty arrays
Number([]); // 0
String([]); // ""
Boolean([]); // true

// arrays with stuff in them
Number(["a"]); // NaN
Number([1]); // 1
Number([2]); // 2
Number([1, 2]); // NaN
Number([null]); // 0
Number([true]); // NaN
String(["a"]); // "a"
String(["a", 1]); // "a,1"
String([null]); // ""
Boolean([false]); // true

// empty objects
Number({}); // NaN
String({}); // "[object Object]"
Boolean({}); // true
```
Most JavaScript-specific WTFery actually depends, deep down, on these conversions.

## Final notes

Much of this isn't JavaScript-specific. The many examples of JavaScript shenanigans are often simple floating-point errors, which can be found in any programming language that uses floating point numbers. (Please stop using these as examples of why JavaScript is bad. There's enough wrong about JavaScript to be mad about without bringing in floating point arithmetic.)

The true WTF is not necessarily that JavaScript can do automatic type conversion, but the absolute bonkers scale of how much it will convert, and to what.

### What do I even do about this?

If you're coming from a strongly-typed language and/or felt like crying several times during this article, I suggest [TypeScript](https://www.typescriptlang.org/).

If you can't or don't want to use TypeScript, make sure to follow these two rules:

1. Always use strict equality (`===`)
2. Always explicitly convert types, rather than allowing JavaScript to implicitly convert them *for* you

JavaScript can't break your stuff with automatic type conversion if you've made sure things are the right type to begin with.

---

You made it to the end! Congrats for wading knee-deep into JavaScript's type system. Have a picture of a squirrel eating at a tiny table for your trouble. (via [reddit](https://www.reddit.com/r/aww/comments/w9lz4q/this_squirrel_likes_to_hang_out_on_my_porch_so_i/))

![A squirrel eating at a table, there's a tiny bucket and some corn.](https://preview.redd.it/upwo5mwqk5e91.jpg?width=960&crop=smart&auto=webp&s=ece2e70fdc67895c29801e1c147c8f961ee2219f)


[The other half of the Reddit post is discussed here](https://dev.to/tchaflich/javascript-wtfs-explained-number-type-and-floating-point-2130).