Overview of JavaScript Types

Let's start with a quick review of JS types.

There are two kinds of data types in JS, primitive types and object types.

Primitive types are the following:

  • undefined
  • null
  • boolean
  • string
  • number
  • symbol

And every thing else in the language is an object type.

As we saw, earlier JavaScript variables can hold any type of data. Another way of saying this is that JavaScript is loosely typed. So we have data types in JavaScript, and variables hold whichever we assign to them, but then how do we figure what the data type that a variable is holding?

We get the data type of a variable through the typeof operator. Let's say we have a string, message:

let message = "some string";

Using the typeof operator on it tells us it's holding a string. And that value is also expressed as a string:

console.log(typeof message); // "string"

We can use typeof directly on the values themselves, also. For example the 42:

console.log(typeof 42); // "number"

Lets try some more values, such a empty variable, the value true, and the window:

console.log(typeof var someVariable);
console.log(typeof true);
console.log(typeof window);

What is very important in JS is how one type is converted to another. We have to be familiar with how a piece of data is changed from one type to another to prevent potential errors in our program.

Converting between types

There are two ways in which values are converted to other types in JavaScript:

  • Explicit conversion: via functions such as String().
  • Implicit conversion (automatic conversion): happens when an operation receives operands/parameters that it can’t work with.

Explicit conversion between types

The function associated with a primitive type explicitly converts values to that type:

Boolean(0) // false Number('123') //123 String(123) // ‘123'

You can also use Object() to convert values to objects:

typeof Object(123) // ‘object'

Coercion (automatic conversion between types)

For many operations, JavaScript automatically converts the data if their types don’t fit together. This kind of automatic conversion is called coercion.

For example, the multiplication operator coerces its operands to numbers:

'1' * '2' // 12

Many built-in functions coerce, too. For example, parseInt() coerces its parameter to string (parsing stops at the first character that is not a digit):

parseInt(123) // 123

Be aware of implicit coercion of types

JavaScript has an automatic type conversion feature. When you do operation on an "unexpected value", JavaScript will silently try to convert/ coerce the unexpected types to the ones it expects.

Let's see what happens if you mix 2 different types (e.g. String and Number). What do you think, will it work?

console.log("10" + 20); //=> "1020”

[Run code] Yes in fact, when values of two types are combined in JavaScript, one type ‘wins’ so to speak.

We got a taste of this in the last video where we saw that we could concatenate strings with the + operator.

In this case, when strings and numbers are combined, no matter whether you add the number before or after the string, the string type wins out or in other words, the number is converted into a string. This converting of one type to another is called coercion.

Coercion within conditionals

I want to give you a short lesson about types and it’s probably the only time in the course I want to tell you to commit something to memory. It’s not much to remember and will serve you well in your JavaScript career.

You’ll become more familiar with this as time goes on, and can always look this information up, but the sooner you memorize what I’m about to tell you the better.

But this implicit coercion we just saw also takes place for any conditional that you’ll write. Any value being evaluated within a conditional, meaning the parentheses of an if statement / else if, a switch statement or before the ? or a ternary, JavaScript will automatically try to convert this to a boolean.

What is important here is that any value we provide to a conditional doesn’t have to be a boolean. There is a more predictable outcome when we use booleans in conditionals, so we can more easily reason about what should be the outcome, but it may not be a boolean for one reason or another. It could be a string, number, function or any other data type.

Review of conditionals

So first let's take a step back and be clear about how conditionals work in JS. This is something that trips up developers. Let’s take a simple if conditional:

if (value) {
  // if true, do something with value
}

This looks simple enough, but we can make it more clear what this actually does (and this applies to all conditionals, such as ternaries)

In conditionals, JS tries to convert the value, regardless of what data type it is into a Boolean and then it compares that value with true. If that result is true, then the condition passes. We can express how JS looks at it like this:

if (Boolean(value) === true) {
  // if coerced to true and equal to true, condition passes
}

All values either truthy / falsy

JavaScript will take any data type within a conditional and turn it into true or false. The question is—how do we know which it will be?

This leads us to two very important concepts when determining the outcome of any conditional in JS: truthy and falsy.

We can divide all data in JavaScript that’s not a boolean (true / false) in to one of these two categories: truthy or falsy.

When a value is truthy, that means it will be coerced to the boolean true in a conditional. On the other hand, if a value is falsy, it will be coerced to the boolean false.

But enough explaining, let’s take a look at some examples. For example a simple if else, passing in a non-boolean value, a string. Based on what we know now about how conditionals in JS work, when 'hello' is coerced to a boolean, is the boolean true, our if condition will be met and we'll see the text run. Otherwise, if it's not met, if 'hello' is coerced to false, our else condition will run and we'll see 'skipped'.

Before we run this, what do you think the outcome will be?

if ("hello") {
  console.log("run!");
} else {
  console.log("skipped");
}

If we run, this, we see 'run'. So based off of what we already know, we only knew we'd only see run if the conditional evaluated to true. So ‘hello’ was coerced to true by JavaScript and according to our previous definition it is a truthy value.

Now what if we replace the string 'hello' with a number, say, 0? What will the outcome of the conditional be and therefore what do you think 0 is--either truthy or falsy?

if (0) {
console.log(‘run!)
} else {
console.log(’skipped’)
}

Here we see 'skipped', meaning 0 was coerced to the boolean false, causing the else condition to run instead. This coercion tells us that 0 is a falsy value, meaning, it becomes false when coerced to a boolean.

Rules for truthy / falsy

So after running these two examples, you might be asking how do we keep all of these values straight? If we don't pass a boolean value to a conditional, how do we know what the outcome of the conditional will be before we actually run it, that is, at runtime? As JS developers, do we have to run every single non-boolean value through a conditional first to see whether it is truthy or falsy?

No, in fact, to keep everything straight you just have to remember 6 values:

false
0 (zero)
‘’ / "" / `` (empty strings in single, double quotes and template literals)
null
undefined
NaN

These are all of the falsy values. Remember these and you’ll keep everything straight. Why? Because as I mentioned, everything that is not falsy is truthy.

The most important ones you will encounter a great deal is false, null, and undefined. False is obvious, but most of the time, we are going to be checking for undefined or NaN.

Why does JS behave like this?

You might be wondering why JS behaves like this? Why are values coerced to a boolean instead of throwing an error and telling us we need to supply a boolean?

The reason for the silent failures is historical: JavaScript did not have exceptions until ECMAScript 3. Since then, its designers have tried to avoid silent failures.

Tips to handle truthy / falsy values

Truthy and falsy values can trip up the most experienced developers. So how do we deal with them? How do we avoid running into issues with them? Here are some tips to avoid problems or unexpected results when comparing JS data:

  1. Avoid direct comparisons

It’s rarely necessary to compare two truthy and falsy values when a single value will always equate to true or false:

// instead of
if (x == false) // ...
// runs if x is false, 0, '', or []

// use
if (!x) // ...
// runs if x is false, 0, '', NaN, null or undefined
  1. Use the triple equals ===, also known as the strict equality operator

We want to use the strict equality (or strict inequality, !==) comparisons to compare values and because they don't allow type conversion. This is as compared to the double equals, == or loose equality operator which will convert types at times:

// instead of
if (x == y) // ...
// runs if x and y are both truthy or both falsy
// e.g. x = null and y = undefined

// use
if (x === y) // ...
// runs if x and y are identical...
// except when both are NaN
  1. Convert to real Boolean values where necessary

Any value can be converted to a real Boolean value using a double-negative !! to be absolutely certain a false is generated only by false, 0, "", null, undefined and NaN:

// instead of
if (x === y) // ...
// runs if x and y are identical...
// except when both are NaN

// use
if (!!x === !!y) // ...
// runs if x and y are identical...
// including when either or both are NaN

Or to be even more explicit, use the Boolean function for more declarative type conversion:

if (Boolean(x) === Boolean(y)) {
  ...
}