What is Type Coercion in JavaScript? A Re-Introduction

A re-introduction to type coercion in JavaScript.

What is type coercion in JavaScript?

Enjoy the reading!

What is type coercion in JavaScript? JavaScript primitives

JavaScript builds upon a bunch of fundamental units. You should already be familiar with some of them, like strings and numbers:

var greet = "Hello";
var year = 89;

Strings and numbers are part of the so called “primitives” of the language. The complete list is:

  • String
  • Number
  • Boolean
  • Null
  • Undefined
  • Object
  • Symbol (added in ES6, won’t be covered here)

Booleans represent values that could be either true or false. null on the other hand is the intentional absence of a value. It is usually assigned to a variable for signalling that the binding will be later populated with something meaningful.

var maybe = null;

And then there is undefined which means that a variable has still nothing attached to it:

var name;
console.log(name)
undefined

null and undefined look pretty similar yet they are two distinct entities so much that developers are still unsure which one to use.

If you want to find out the type of a JavaScript entity you can use the typeof operator. Let’s try with a string:

typeof "alex"
"string"

with a number:

typeof 9
"number"

with a boolean:

typeof false
"boolean"

on undefined:

typeof undefined
"undefined"

and with null:

typeof null
"object"

which gives us a suprising result! null looks like an object but in reality it is an historic bug in JavaScript, lying there since the language was born. JavaScript has always had a bad reputation because of these things. And that’s just the beginning.

What is type coercion in JavaScript? Stranger things

There are some strange rules for converting between one type and another in JavaScript. Let me give you a bit of context. Let’s make an example in Python. The following instruction in Python:

'hello' + 89

gives you a clear error:

TypeError: can only concatenate str (not "int") to str

While in JavaScript only the sky is your limit:

'hello' + 89

in fact gives:

"hello89"

Things looks even more stranger if we try adding an array to a string:

'hello' + []

gives:

'hello'

and:

'hello' + [89]

give a suprising:

"hello89"

Looks like there is some kind of logic behind this conversion. It works even with more crowded arrays:

'hello' + [89, 150.156, 'mike']

gives:

"hello89,150.156,mike"

These two lines of JavaScript are enough to make a Java developer run away. But this behaviour in JavaScript is 100% intentional. So it is worth exploring the most glaring cases of implicit conversion in JavaScript, also know as type coercion.

What is type coercion in JavaScript? When a number becomes a string

Some programming languages have a concept called type casting which means more or less: if I want to convert a number or another entity to another type then I have to make the conversion explicit. It is possibile in JavaScript too. Consider the following example:

var greet = "Hello";
var year = 89;

If I want to convert explicitly I can signal the intention in my code:

var greet = "Hello";
var year = 89;

var yearString = year.toString()

or:

var greet = "Hello";
var year = 89;

var yearString = String(year)

and then I can concatenate the two variables:

greet + yearString;

But in JavaScript there is a subtle mechanic called implicit conversion, kindly offered by JavaScript engines. The language does not prevent us from adding numbers and strings:

'hello' + 89

gives:

"hello89"

But what’s the logic behind this conversion? You may be surprised to find out that the addition operator + in JavaScript automagically converts any of the two operands to a string if at least one of them is… a string!

And you may find even more surprising that this rule is set in stone in the ECMAScript spec. Section 11.6.1 defines the behavior of the addition operator which I’ll summarize here for you own sake:

If x is String or y is String then return ToString(x) followed by ToString(y)

Does this trick work only on numbers? Not really. Array and objects are subject to the same conversion:

'hello' + [89, 150.156, 'mike']

gives:

"hello89,150.156,mike"

And how about:

'hello' + { name: "Jacopo" }

To find the solution you can do a quick test by converting the object to a string:

String({ name: "Jacopo" })

which gives:

"[object Object]"

So I’ve a feeling that:

'hello' + { name: "Jacopo" }

will give:

"hello[object Object]"

Hold on! What’s that?

What’s the meaning of [object Object] in JavaScript?

“[object Object]” is one the most popular JavaScript quirks.

Almost every JavaScript entity has a method called toString() which is some ways is a courtesy of Object.prototype.toString.

Some types like arrays implement a custom version of toString() so that the value gets converted to string when the method is called. For example Array.prototype.toString overwrites Object.toString() (also called method shadowing).

But when you call toString() on a plain JavaScript object the engine gives “[object Object]” because the default behaviour of Object.toString() is to return the string object followed by the entity type (Object in this case).

But now let’s point our attention to JavaScript comparison operators which are as weird as their arithmetic counterparts.

What is type coercion in JavaScript? Equal equal or not equal?

There are two main families of comparison operators in JavaScript.

First we have what I like to call “weak comparison“. It’s the abstract comparison operator (double equal): ==.

Then there’s a “strong comparison” which you can recognize from the triple equal: === also called strict comparison operator. Both of them behave in a different way from the other.

Let’s see some examples. First thing first if we compare two strings with both operators we get consistent results:

"hello" == "hello"
true

"hello" === "hello"
true

Everything looks fine.

Now let’s try to compare two different types, a number and a string. First with the “strong comparison”:

"1" === 1
false

Makes sense! The string "1" is different from the number 1. But what happens with a “weak comparison”?

"1" == 1
true

True! Now it doesn’t make any sense. Unless this behaviour has something to do with the implicit conversion we saw earlier.

What if the same rules apply? Bingo. The ECMAScript spec strikes again. Turns out the abstract comparison operator makes an automatic conversion between types, before comparing them. This is an abstract of the spec:

The comparison x == y is performed as follows: …omissis…

if x is String and y is Number return the result of the comparison ToNumber(x) == y

The spec says: if the first operand is a string and the second operand is a number then convert the first operand to a number. Interesting.

The JavaScript spec is full of this crazy rules and I highly encourage digging deeper into it.

In the meantime unless you have a strong reason to do otherwise avoid the abstract comparison operator in your JavaScript code. You will thank yourself later.

And how about the “strong comparison”? The spec at Strict Equality Comparison says that no automatic conversion is made before comparing values with the triple equal ===. And using the Strict Equality Comparison in your code will save yourself from silly bugs.

What is type coercion in JavaScript? Wrapping up

There are seven building blocks in JavaScript, namely String, Number, Boolean, Null, Undefined, Object, and Symbol. These types are called primitives.

JavaScript developers can manipulate these types with arithmetic and comparison operators. But we need to pay particular attention to both the addition operator + and the abstract comparison operator == which by nature tend to convert between types.

This implicit conversion in JavaScript is called type coercion and is defined in the ECMAScript spec. Whenever possibile in your code use always the strict comparison operator === instead of ==.

As a best practice always make clear when you intend to convert between one type and another. For this purpose JavaScript has a bunch of built-in objects which mirror the primitive types: String, Number, Boolean. These built-in can be used for converting explicitly between different types.

Thanks for reading!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.