typeof ([] + {})

Understanding Javascript: Types

7 min

This post marks the beginning of a series of articles about Javascript, which I hope will be a powerful journey of evolution for all of us. As I said in my first post, I want to cover a little more in-depth topics about this universe and not just focus on the basics.

But let's start with small steps, gradually moving towards more complex areas, so you can have a broad mastery of the language and how it works. With that in mind, I want to talk in this article about something often overlooked when discussing Javascript, which is typing.

What do you mean, typing in Javascript?

Well, I imagine you know Typescript, after all it's becoming almost mandatory, right? And you may have already read, heard or even said that “Ah, Typescript is Javascript with types”. That's cool, but that's not right!

Javascript does have types, but, of course, it is different from languages like C, Java, Rust, etc. Languages like these have what is called static typing. Languages like Javascript, Python or Ruby have dynamic typing. And what does this mean in practice?

It means that in statically typed languages, variables and expressions have a type defined at compile time, and this type cannot be changed. In dynamically typed languages, values have types, and variables can receive any value, and therefore, the type of a variable is determined only at run time.

That's why Javascript does have types! So let's talk about what they are.

Everything is an object, right?

I know it can be tempting to think that in Javascript everything is an object, because almost all values allow you to access properties or methods. For example, strings allow you to use methods like charAt(), among others. But that doesn't mean everything is an object. In fact, there are primitive types and objects.

Primitive types

They are immutable values, which means that when you do some operation on them, an entirely new value is actually being returned, instead of modifying the existing value. There are 7 primitive types:

Objects

In short, objects act as a collection of properties that hold values, but there are different types of objects. Basically, we can divide objects into two categories: literal objects, which are defined by curly braces ({…}) and standard global objects, which are native to the language, such as Math, JSON, String, Number, Date, etc.

Arrays are also objects, but with special characteristics, just like functions, which are considered callable objects.

Auto-boxing

Primitive values are not objects, and therefore do not have any associated methods or properties. And now you may be asking yourself: “So how do we use primitive values as if they were objects?” What happens is that when properties or methods are accessed on primitive values, Javascript temporarily encapsulates the values in a corresponding standard object. For example, when running the code:

"ABC".charAt(0)

it's as if Javascript executed:

new String("ABC").charAt(0)

The exception is null and undefined values, which do not have corresponding objects.

Messing with different types

We often need to work with data of different types or we need to transform a value from one type to another, for example, when we have a number stored as a string and we want to convert it into a number to perform calculations.

When we perform operations with values of different types, Javascript automatically tries to convert one of the values to a compatible type before performing the operation. This is known as type coercion.

Implicit and explicit

Some may say that coercion is only when there is an implicit type conversion, that is, done automatically by Javascript. But here we will treat everything as type coercion, whether implicit or explicit.

And it's important to talk about this because many bugs can arise precisely from this implicit coercion behavior. For example, what happens if you are expecting to receive two numbers to perform a calculation, but suddenly one of the numbers coming from the API is actually a string? Or if you are making a comparison between two values, one of which is an array and the other a boolean?

To talk about the different behaviors that can occur under coercion, we would need much more than just this article, so I will just mention some of the most common cases that you can/should pay attention to.

Conditional expressions

When we do any type of conditional comparison, whether with if, the ternary operator (? :), logical operators (&&, ||), etc., Javascript will internally coerce the values to boolean. But this boolean value will not necessarily be returned to your program.

Operations between values

When performing an operation between two values, keep in mind that, if they do not have the same type, Javascript will perform coercion for the operation to be performed. Therefore, things like the following happen:

['a', 'b', 'c'] + 100  // 'a,b,c100'
'abc' + {} // 'abc[object Object]

And so on. So, be careful when performing operations between values of different types.

The mystery of NaN (it's not milk!)

A very peculiar value in Javascript is NaN (which would be an acronym for “Not a Number”), and it is used to represent invalid numeric values. For example, when performing the 0/0 division the result is exactly NaN.

NaN === NaN  // false
typeof NaN // "number"

Yeah, NaN is of type number (which may be strange at first, but is actually reasonable, bearing in mind that it represents invalid numeric values) and is not equal to itself. In fact, it is the only value in Javascript that is not equal to itself.

You can create your own function to check if a value is NaN, or use the isNaN() function, standard in Javascript from ES6 onwards.

Comparison of values with == and ===

There are two types of value comparison in Javascript: == (loose equality) and === (strict equality). The difference between them is in the way they treat type coercion. The == operator performs type coercion before making the comparison, so that both values have the same type, while the === operator does not perform type coercion and compares values and types exactly as they are. It is important to understand this difference to avoid unexpected behavior in your value comparisons.

Most of the time, it is said that it is good practice to just use ===, to avoid type coercion bugs. But there are cases where you can use the “double equals” comparison to your advantage. See the two cases below, where you want to check if a variable has no value (that is, it is null or undefined):

if (data === null || data === undefined) {}

if (data == null) {}

The two comparisons will behave in the same way, this is because “double equals” also returns true when the two compared values are null and undefined.

That's it for today!

I know this topic is confusing, but I hope it wasn't too confusing! As I said, the issue of type coercion has many details, which would not fit here in this article without it becoming a book. If you want to see more details, I left some links at the end. Or if you prefer to laugh a little, you can watch these two videos: wat and JavaScript is Weird.

Thank you for staying this far, and if you find any errors in the information or have any suggestions, please get in touch!

In the next article we will talk about one of the very interesting features of Javascript, which is commonly known as closure. Until later!

Sources: