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.
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
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
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!');
}
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
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
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")
}
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
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
- explicit: This is when a developer converts types by writing the appropriate code to do so e.g
"0" + String(5) == "05"
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 theString
function e.g
const height = 5.8;
console.log("Hello I am " + height + "ft tall") // => "Hello I am 5.8ft tall"
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"
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 theBoolean
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
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"
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"
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
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