I was intrigued by one the question asked on my So you think you know JavaScript article.

js {} + []; // returns 0 ?? 🤔 I admit that I didn't know the correct answer at that moment, but instead of blaming and criticising the JavaScript coercion and type system, I delved into the specs to find the definition of Addition operator. At first, the specs didn’t make much sense to me. I found it verbose. May be my brain wasn't trained on reading the specs. Yeah, let's be honest, how many of us read the specs when there's a question about JavaScript? We have our beloved StackOverflow. Right?

Well, I was desperate to know the answer. I didn't want to be in the category of those devs who consider coercion as some internal magic and dangerous, to be shunned or avoided.

So, this article is to share my understanding of coercion in JavaScript, and illustrate why coercion's bad reputation is exaggerated and somewhat undeserved—to flip your perspective so you can see its usefulness and power.

JavaScript Type System

JavaScript is a dynamically typed language where variables don't have types—values have types. JavaScript type system doesn't enforce that the variable always holds the same initial type it starts out with.

```js // variable 'a' starts out with holding a string value type. var a = 'some string';

// you can change the type in the next line and it's completely valid // Now, the variable 'a' holds the value of type number a = 10; ```

I always see this as one of the strongest points of the JavaScript type system. But some devs from strongly typed language may find this as a flaw in the language and object the usage of word 'type'. And I think that's one of the many reasons we are perpetually exploring the ways (Flow and TypeScript) to put a layer of type system on the language. In my opinion, it's like we are duck-taping JavaScript into a system which is not in the DNA of the language.

I believe we should always strive to learn the fundamentals and think alike JavaScript. Instead of flowing against it, let's flow towards it and see why the aforementioned question shouldn't be overhyped thinking that JavaScript is weird.

Let's quickly revisit what we know so far about JavaScript types and then we will deep dive into coercion in the later sections.

JavaScript has seven built-in types: - null - undefined. - string - number - boolean - object - symbol

Except object, all other types are called 'Primitives'. typeof operator is a nice built-in utility to check the types. Keep in mind that typeof always returns a string type.

```js typeof 'you are awesome!' // 'string' typeof 42 // 'number' typeof true // 'boolean' typeof undefined // 'undefined' typeof {name: 'aman'}. // 'object' typeof Symbol() // 'symbol'


typeof function foo(){}. // 'function' typeof [] // 'object' ```

You will be wondering why invoking typeof on function and array return 'function' and 'object' respectively. The reason is that functions and array are subtypes of the object type. And because of this, you are able to add properties to the function and invoke some of the methods which an object type has—toString(), and valueOf().

```js function foo(a,b){}

// you can add any property on foo object. foo.someProperty = 'I am a property on foo function';

// function object has 'length' property set to number of formal params it takes when declared foo.length; // 2

// invoke 'toString()' foo.toString(); // "function foo(a,b){}"

// invoke 'valueOf' foo.valueOf(); // return this -> the function itself ```

Bonus: According to the typeof spec, if an object type implements an internal [[Call]] method, then typeof check returns "function". Object literals and array don't implement it, but the function does.

There are a few gotchas you need to be aware of with typeof operator. As you may have noticed that I have excluded typeof null from the list above. The reason is that null is a special case where typeof operator returns 'object'. It's the only primitive in JavaScript which is 'falsy' and returns 'object' from typeof check.

Note: null is the only primitive in JavaScript which is 'falsy' and returns an ’object' from typeof check.

js typeof null; // 'object';

So, how would you go about checking the null type explicitly? You may need a statement like: ```js var a = null; !a && typeof a == 'object'; // true

// Or you can use strict equality comparison a === null; // true ```

Let's consider one more quirk with typeof operator: js var a; typeof a; // 'undefined' typeof b; // 'undefined' In JavaScript, var declared variables get assigned a value of undefined when they have no current value. And that's the reason typeof operator returns 'undefined'. But if you see we haven't declared the variable b anywhere, but typeof operator still manages to print 'undefined'. It's because JavaScript engine is playing safe and instead of returning some error, it returns undefined.

As I said knowing these difference is like aligning your mind with JavaScript engine. Every language has some corner cases. JavaScript is not an exception. Instead of making a joke about the language, I think it's crucial to understand them so that you can take better decisions in your program.

Now, let's move on to the next part of understanding coercion in JavaScript.

Coercion

Coercion aka 'type conversion' is a mechanism of converting one type to another. In statically (strongly) typed language this process happens at compile time whereas coercion is a run-time conversion for dynamically typed languages.

In JavaScript, we can have two types of coercion: "implicit" and "explicit". As the name implies, implicit coercion is the one which happens as a less obvious side effect of some intentional operation. On the contrary, the explicit conversion is obvious from the code that it is occurring intentionally.

```js var a = 10;

var b = 'programmer' + a; // implicit coercion var c = you owe me ${a} dollars. // implicit coercion

var d = String(a); // explicit coercion var e = Number('42') // explicit coercion ```

Have you ever wonder how coercion works internally? That's where things get interesting. But before we can explore the internal procedures, we need to understand some of the operations which are defined in ECMAScript 2020 section 7 called Abstract operation. These operations are not part of the language but are used to aid the specification of the semantics of JavaScript language. You can think of these operations as conceptual operations.

Abstract Operations

Every time a value conversion happens, it is handled by one or more abstract operation with some rules defined in the spec. Here we will look into three abstract operations: ToString, ToNumber and ToPrimitive.

Abstract operations are used to aid the specification of the semantics of JavaScript language.

ToString

Whenever we coerce a non-string value to a string value, ToString handles the conversion as in section 7.1.12 of the specification. Primitive types have natural stringification. The table looks like:

js // ToString abstract operation (string conversion) null -> 'null' undefined -> 'undefined' true -> 'true' false -> 'false' 52 -> '52'

For regular object and array, the default toString() is invoked which is defined on the Object.prototype

```js var a = {language: 'JavaScript'}; a.toString(); // "[object Object]"

[].toString(); // "" ```

You can also specify your own toString method to override the default return value:

```js var a = { language: 'JavaScript', toString(){return 'I love JavaScript'} };

a.toString(); // "I love JavaScript" ```

ToNumber

Whenever a non-number value is supplied in an operation where a number was expected, such as a mathematical operation, ES2020 defines a ToNumber abstract operation in section 7.1.3. For example

js // ToNumber abstract operation (number conversion) true -> 1 false -> 0 undefined -> NaN (not a valid number) null -> 0

For object and array, values are first converted to their primitive value equivalent (via ToPrimitive operation) and the resulting value is then coerced into number according to the ToNumber abstract operation.

ToBoolean

ToBoolean is a little simpler than ToString and ToNumber operation as it doesn't do any internal conversion. It only performs a table lookup as mentioned in section 7.1.2.

Argument type | Result --- | --- undefined | false null | false boolean | return argument number | if argument is +0, -0 or NaN, return false; otherwise true string | if argument is empty string, return false; otherwise true symbol | true object | true

ToPrimitive

If we have non-primitive type (like function, object, array) and we need a primitive equivalent, ES2020 defines ToPrimitive in section 7.1.1.

ToPrimitve operation takes two arguments: input and hint(optional). If you are performing a numeric operation, the hint will be a 'number' type. And for string operation (like concatenation), the hint passed will be a string. Note that ToPrimitive is a recursive operation which means that if the result of invoking ToPrimitive is not a primitive, it will invoke again until we can get a primitive value or an error in some cases.

Now let's look at the algorithm behind the ToPrimitive operations.

Every non-primitive can have two methods available: toString and valueOf. If 'number' hint is sent, valueOf() method is invoked first. And if we get a primitive type from the result then we are done. But if the result is again a non-primitive, toString() gets invoked. Similarly, in the case of 'string' hint type, the order of these operations is reversed. If the invocation of these two operations doesn't return a primitive, generally it's a TypeError.

Visually, The order can be seen as follows:

```js // ToPrimitive Abstract Operation

// hint: "number" valueOf() toString()

// hint: "string" toString() valueOf() ```

To make it more clear here's the flow chart diagram of the algorithm we discussed above:

Alt Text

Now armed with this new knowledge of abstract operations, it’s the time to answer a few questions confidently.

Testing our knowledge

js // Why the following expression produces '5' as a result? [] + 5; // '5'

As per the specification in section, the addition operator ‘+’ performs string concatenation or numeric addition based on the argument type. If either of the argument is string, it will perform string concatenation. It's called operator overloading. Now let see how did we end up getting the string ”5”?

We were expecting a primitive type but end up getting an array as one of the argument. Consequently, ToPrimitive abstract operation is performed with "number" passed as a hint. Referring to the ToPrimitive diagram above, we can assert the following steps will take place to get the result.

  • [].valueOf() // returns [];
  • As, [] is not a primitive, engine will invoke [].toString() resulting in an empty string.
  • Now the expression reduces to "" + 5.
  • As we mentioned that addition operator performs string concatenation when either of argument is a string type.
  • So, 5 will be implicitly coerced to “5” via ToString abstract operation passing 'string' as a hint.
  • Finally the expression reduces to "" + "5" resulting in value "5".

js [] + 5; // ToPrimitive is invoked on [] // "" + 5; // "" + "5"; // "5"

Now, that's a moment of inner satisfaction. Isn't it? I don't know about you but when I figured this out, I was delighted💡😀.

Before we wrap up, let's quickly demystify some of the following expression to strengthen our grip. I am going to reduce the expression from top to bottom (via abstract operations) to reach the result.

```js [] + []; // ToPrimitive is invoked on both operands // "" + ""; ""


[] + {}; // ToPrimitive is invoked on both operands // "" + "[object Object]"; "[object Object]"


'' - true; // There's no operator overloading for subtract operator. //ToNumber is invoked on both the operands (already primitive) // 0 - 1; -1


1 < 2 < 3; // (1 < 2) < 3;
// true < 3; // ToNumber is invoked on true -> 1 // 1 < 3; true;


3 < 2 < 1; // true ooops! // (3 < 2) < 1; // false < 1; // ToNumber is invoked on false -> 0 // 0 < 1; true ```

Now is the right time to answer the question which basically led me to write this article.

js {} + []; // 0 🤔?? Here '{}' is not an empty object but just an empty block {}. So, JavaScript engine ignores it and left with + [] statement to execute. It’s a numeric operation and hence a ‘number’ hint will be passed to convert this empty array into a primitive value, which is an empty string. Finally, the empty string is coerced again via ToNumber operation leading to a value of 0. 😀

js {} + []; // empty block is ignored // + []; // + ''; // + 0 ; 0

Summary:

  • JavaScript is a dynamically typed language where values have type—not the variables.
  • Coercion aka “type conversion” is a procedure of converting one value type to another; it happens at compile time for JavaScript.
  • Coercion can be of two types: implicit and explicit.
  • Abstract operations are the keys to understanding coercion. They are not actual operation in the language but are used to aid the specification of the semantics of JavaScript language.
  • Whenever we receive a non-primitive value for an operation where a primitive type was expected, ToPrimitive abstract operation is invoked.
  • For any non-primitive, ToPrimitive invokes two methods: valueOf() and toString(). Depending upon the hint passed, valueOf() followed by toString() is invoked for the ‘number’ hint , and vice versa for “string”.

Conclusion:

Dynamic nature of JavaScript is one of its core features. Understanding how coercion works internally can help us write robust code. Every language has some quirks and it’s our responsibility as a developer to be mindful of these caveats. Instead of finding flaws, we need to strive for learning the semantics of the language and work towards it.

Hope you liked the article and if that’s a boolean true, a few ❤️ will make me smile 😍.