DEV Community

Mainasara Al-amin Tsowa
Mainasara Al-amin Tsowa

Posted on

Avoiding Weird Javascript Behaviour (true + true === 2 but true !== 1)

Every single programming language is designed to be intuitive and Javascript is no exception, but it does have some crazy quirks that make it stand out, one of which is its weird behavior with types and I am pretty sure you have seen the memes.

javascript meme

This can be traumatizing the first time you see it but it does not have to be, so I want to shed a little light on it and help you avoid running into such.

How Javascript types work

Javascript is a very very loosely typed language, this is not bad at all but in some situations, it can cause very bad head scratching. One of these moments occurred when I first saw this a few years back.

true + true === 2 // => true

true !== 1 // => true
Enter fullscreen mode Exit fullscreen mode

At first glance, it looks very wrong but don't fret there is nothing wrong here, that is just how javascript works. Javascript handles types in a very different way, it has only 6 primitive types and all the code you write is represented by one of them.

  • boolean

  • number

  • string

  • object

  • function

  • undefined

This alone is not enough to cause such strange behavior but the way they are handled does, javascript always converts types to best fit what they are being used for, this is called Type Coercion and sometimes it changes value types to those not intended by the developer, this is why true + true === 2.

The binary + operator is primarily meant to add numbers but when it is faced with boolean values it's forced to convert them to numbers thus true becomes 1 which makes true + true become 1 + 1 and the expression is turned into

1 + 1 === 2 // => true
Enter fullscreen mode Exit fullscreen mode

But the second expression true !== 1 seems to defeat what I just explained above but it really makes perfect sense too. The !== operator performs a strict comparison so it checks both value and type but since true is a boolean and 1 is a number they are not strictly equal thus the expression is true.

Truthy and Falsy

Every value in javascript has its own boolean value (truthy/falsy), these values are used in operations where a boolean is expected but not given, you have most likely used this feature before but never knew what was going under the hood.

Example:

const array = [];

if(array){
    console.log('Truthy!');
}
Enter fullscreen mode Exit fullscreen mode

In the code above, array is not a boolean but because the value is "truthy", the if block will be executed.

Falsy vs false

Falsy values are values with an inherent boolean false, the following are falsy values.

  • 0
  • '' or ""
  • null
  • undefined
  • NaN

Example:

const zero = 0;
const emptyString = "";

if(!zero){
    console.log("0 is falsy");
}

if(!emptyString){
    console.log("An empty string is falsy")
}

console.log(NaN || 1); // => 1
console.log(null || 1); // => 1
console.log(undefined || 1); // => 1
Enter fullscreen mode Exit fullscreen mode

Note that the value false is falsy but falsy values are not false and the right way to differentiate them is by using strict comparison operators.

Example:

0 == false // => true
0 === false // => false
Enter fullscreen mode Exit fullscreen mode

Truthy vs true

Every value that is not falsy is considered truthy, these include

  • strings

  • arrays

  • objects

  • functions

Example:

function somethingIsWrong(){
    console.log("Something went horribly wrong")
}

function callback(){
    console.log("Hello From Callback");
}

const string = "Hello world!"
const array = [1,2,3];
const object = {};

if(string){
    console.log(string) // => "Hello world!"
    const functionToCall = callback || somethingIsWrong
    functionToCall() // => "Hello From Callback"
    console.log(array || "That was not meant to happen")
    console.log(object || "This is strange")
}
Enter fullscreen mode Exit fullscreen mode

Type coercion

Type coercion is the process of converting one type to another and every value in javascript can be coerced.

There two types of coercion:

  • implicit: This is done automatically when the need arises e.g
"0" + 5 == "05" // => true
Enter fullscreen mode Exit fullscreen mode

The above example shows implicit coercion in its most recognizable form, the + operator is not only used to add numbers but strings too so when it is told to add a number to a string it has to convert the number to a string first, which changes 5 to "5" and the expression becomes.

"0" + "5" == "05" // => true
Enter fullscreen mode Exit fullscreen mode
  • explicit: This is when a developer converts types by writing the appropriate code to do so e.g
"0" + String(5) == "05"
Enter fullscreen mode Exit fullscreen mode

Type conversions

When a value is coerced, it undergoes one of three conversions.

  • ToString: This is triggered implicitly by the + operator and explicitly by calling the String function e.g
const height = 5.8;
console.log("Hello I am " + height + "ft tall") // => "Hello I am 5.8ft tall"
Enter fullscreen mode Exit fullscreen mode

The + operator implicitly converts the floating point number to a string before concatenating them.

const height = 5.8;
console.log("Hello I am " + String(height) + "ft tall") // => "Hello I am 5.8ft tall"
Enter fullscreen mode Exit fullscreen mode

Here we use the String function to explicitly convert the floating point number to a string.

  • ToBoolean: This is triggered implicitly by either their context or by logical operators (!, || and &&) and explicitly by the Boolean function.
if(3){
    console.log("Implicitly by context")
}

if(Boolean(1)){
    console.log("Explicitly by the 'Boolean' function")
}

console.log(!0) // => true

console.log(0 || "Hello") // => "Hello"

console.log(4 && 5) // => true
Enter fullscreen mode Exit fullscreen mode

Note that the || operator does not return the truthy value of "Hello" but the actual string itself

  • ToNumber: Numeric conversion is very tricky because it is triggered by a lot of operators > < <= >= | & ^ ~ - + * / % != ==. Note, that binary + does not trigger numeric conversion and == does not trigger numeric conversion when both operands are strings.

There is a lot more on coercion that I can't explain here, so here is a link to an excellent post on javascript type coercion.

Avoiding this behavior

The best way to avoid this behavior is by type checking and an easy way to do this in vanilla javascript is by using a strict equality operator === when comparing values or checking a variable's type with the typeof keyword.

Example:

without type checking

const number = 21;
const string = "21"

function add100(number){
    console.log(100 + number)
}

add100(number) // => 121
add100(string) // => "10021"
Enter fullscreen mode Exit fullscreen mode

with type checking

const number = 21;
const string = "21"

function add100(number){
    if(typeof number == "number"){
        console.log(100 + number)
    } else {
        console.log("Need a number, not a "+typeof number);
    }
}

add100(number) // => 121
add100(string) // => "Need a number, not a string"
Enter fullscreen mode Exit fullscreen mode

Unfortunately, the examples above can not help you with checking classes, for that you need to use the keyword instanceof.

class ImportantClass {
    constructor(){
        this.importantValue = 1;
    }

    doImportantStuff(){
        console.log(this.importantValue);
    }
}

function useImportantClass(value){
    if(value instanceof ImportantClass){
        value.doImportantStuff();
    } else {
        console.log("Value needs to be of type ImportantClass, not "+typeof value)
    }
}

const value = new ImportantStuff();

useImportantClass(value); // => 1
useImportantClass("Not important"); // => Value needs to be of type ImportantClass, not string
Enter fullscreen mode Exit fullscreen mode

But by far the easiest way to avoid this is by using Typescript. If you haven't heard of it, Typescript is a typed superset of javascript that compiles to normal javascript. In short, Typescript and its compiler help you write code that is very deterministic and I encourage you to click the link if you want to know more because the site explains more than I can.

Thanks for reading!!!

Consider giving me a follow on Twitter

Top comments (3)

Collapse
 
renoirb profile image
Renoir • Edited
(true + true === 2)

Happens because of type coercion occuring right at "+ true" and also because there is more than one equal (not an assignement).

In the same way, if you need a date epoch interger, instead of using

new Date().getMilliseconds()

you can just do

+new Date()

Plus at the beginning of the statement forces type coercion into number.

Dr. Axel Rauschmayer's book Speaking JS is amazing and full of well written explanations. Including what I'm outlining here.

Collapse
 
neutrino2211 profile image
Mainasara Al-amin Tsowa

Thanks, going to fix that

Collapse
 
fabricejumarie profile image
fabricejumarie • Edited

another javascript joke!
null >= 0; --> true
null > 0; --> false
null == 0; --> false