DEV Community

loading...
Cover image for What is Type coercion in JavaScript ?

What is Type coercion in JavaScript ?

Tahir Ahmed Tapadhar
Tech Enthusiast, C#, Javascript, .NET , Cloud, Life-long Learner, Polymath & an aspiring polyglot
・7 min read

It will not be incorrect to say that javascript falsely looks like other programming languages. Unlike languages like C++/Java/C#, javascript is nothing like those languages. It's a different concept, a different mindset, a different paradigm. While many programmers learning javascript or using javascript find some of its elements as they dig into more advanced features odd or difficult to learn or weird. You're going to find that when you truly understand it, how javascript works & how it works the way it does you'll discover that javascript is a deceptively powerful & beautiful programming language. This is also the reason why it is one of the most popular programming language.

We need to understand how javascript is functioning under the hood in order to be able to properly write javascript code; deep advanced javascript code. We're going beyond just the basics. So even though it may seem at moments during this post that we're covering that "Oh, you don't really need to know that!", you'll find as we progress that, fundamental foundational knowledge is going to brighten the path for you, because very quickly you'll see just how important they really are.

What lures devs into learning javascript is understanding it's weird parts. One such concept that we shall talk about today is the Type Coercion in JavaScript. During your time of development using the javascript language, I'm sure you must have encountered something like the below:

console.log(5 + '5'); // '55'
console.log(5 - '5'); // 0

console.log('2' * 2); // 4
console.log('2' / 2); // 1
console.log('2' - 2); // 0
console.log('2' + 2); // '22' 😳huh??..
console.log('' + 2); // '2'

Type Coercion:

Type coercion is the automatic or implicit conversion of values from one data type to another data type(eg. string to number & vice-versa). Type conversion is similar to Type Coercion because they both convert values from one data type to another with one key difference β€” type coercion is implicit whereas type conversion can be either implicit or explicit.
In simple terms, we as a developer don't explicitly perform type coercion. It is done internally by the javascript engine.

What causes type coercion?

First of, if I'm not wrong, you cannot perform mathematical operations on two operands of different type in any(compiled) language. In a compiled language, the compiler itself will throw an error during compilation.

// In any compiled language like C#, this is not possible

var operand1 = 2; // int
var operand2 = '5'; // char
var result = operand1 + operand2; // compilation error

But HEY!! this is JavaScript....it is dynamic, everything is possible hereπŸ˜›. So in javascript, when we perform some mathematical operations on two variables of the same type, the result would be similar to that of any other language. But when the operands are of different types, javascript internally tries to make things even by making both the operands of the same type. This is done by converting either one of the operand to match the type of the second operand.
Considering the above example.


// number 5 is coerced to string '5'
console.log(5 + '5'); //'55'

// string 5 is coerced to number 5
console.log(5 - '5'); //0

In the above example, javascript has coerced the value 5 from number to string making both the operands string type and then concatenates the values together resulting into '5' + '5' => '55'. Similarly, in the second example, javascript coerces one of the operand to match the type of the other. But this time string '5' is coerced to number 5 evaluating to 5 - 5 => 0.

Since JavaScript is a weakly-typed language, values are converted between different types automatically or rather implicitly. It usually happens when you apply operators to values of different types. JavaScript types can be broadly categorized into 2 categories

  • Primitive Types : (number, string, boolean, null, undefined, symbol)
  • Compound/Structural Types : (Object, Array, Map, Set, Date, etc..) basically almost everything made with new keyword

Types of conversion

Irrespective of any type, be it primitive/structural, when type coercion is performed, it converts the operand type into one of these types:

  1. To String
  2. To Number
  3. To Boolean

Conversion logic for primitives and objects works differently, but both primitives and objects can only be converted in these three ways.

Primitive to String coercion:

When adding two operands with the binary(+) operator, the JavaScript engine will try to add the values if both the values are of number type.

console.log(3 + 2); // 5
console.log(20 + 10); // 30

But when either of the operand is a string type, javascript performs coercion on that operand & converts both of them to string. Hence the result is now a concatenation of 2 strings. The order of the operands doesn't matter.

// number,string
console.log(5 + 'hi'); // '5hi'
console.log('5' + 5); // '55'

// boolean, string
console.log('hi' + false); // 'hifalse'
console.log('12' + true); // '12true';

// number, boolean, string
console.log(1 + 'two' + false); // '1twofalse'
console.log(2 + true + '3'); // '2true3'
console.log('Three' + 4 + false); // 'Three4false'

// null, string
console.log(null + '123'); // 'null123'

// undefined, string
console.log('123' + undefined); // '123undefined'

What happens when both the operands are not number, or also if either of them is not a string as well while using the binary(+) operator

console.log(null + undefined); //NaN

When we try to add null and undefined, the JavaScript engine tries to convert the values to integer resulting in NaN.

Primitive to Number coercion:

There are multiple scenarios where a primitive value is coerced to number type. Any primitive is coerced to a number type when the following operators are involved

  • Comparison operators('>','<','>=','<=')
  • Unary operators('+','-')
  • Arithmetic operators('+','-','*','/','%')except for '+' when one operand is a string
  • Bitwise operators('|','&','^','~')
  • Loose equality operator('==')except for when both arguments are already the same primitive
1. Comparison operator
7 <= '3' // false
'5' > 4 // true

// true becomes 1
true > '1' 
1 > 1 // false

// false becomes 0
false < 1 // true

// string 'true' becomes NaN
'true' > 1
NaN > 1 // false

// null becomes 0
null < 5 // true

// undefined becomes NaN
undefined > 10 
NaN > 10 // false
2. Unary operator
+2
// 2

+"123"
// 123

+true
// 1

+null
// 0

+undefined
// NaN
3. Arithmetic operator
'33' - 10
33 - 10 
=> 23

'9' / 3
9 / 3 
=> 3

// false becomes 0
5 / false
5 / 0 
=> Infinity

5 % '3'
5 % 3 
=> 2

// true becomes 1
'true' * 10
1 * 10 
=> 10

// undefined becomes NaN
1 - undefined
1 - NaN 
=> NaN
4. Bitwise operator
true & 0 => 0
true & 1 => 1
true | 0 => 1
true | 1 => 1
true ^ 0 => 1
true ^ 1 => 0
5. Loose equality operator
/*In JS, == operator is very common to compare values. It compares the values based on their values ignoring their types*/
54 == '54'
54 == 54
//true

true == '1'
1 == 1
//true

false == '0'
0 == 0
//true

/* complex cases*/
//null can only be coerced to null and undefined
null == 0
=> false

// 'true' becomes NaN
true == 'true'
=> false

Primitive to Boolean coercion:

Primitives are coerced to boolean when using logical operators like '&&', '||', '!'. The most important point to remember here is even though logical operators do boolean conversions internally, but actually return the value of original operands, even if they are not boolean. The last evaluated operand is returned from each expression.

!(NOT)
!0 => ! (false) // true
!1 => ! (true) // false

&&(AND)
true && '20'
true && true
=> '20'

0 && false
false && false
=> 0

45 && 'hi'
true && true
=> 'hi'

||(OR)
null || 'hello'
false || true
=> 'hello'

null || undefined
false || false
=> undefined

Another interesting thing here is that a primitive type coercion to boolean can also be triggered by the surrounding context, where the type/value is coerced to boolean such as control flows like an if(value) statement. Truthy & Falsy values are the best examples of boolean type coercion. If you don't know what they are you can have a quick read about them here Truthy and Falsy values in JS.

// undefined evaluates to false
if(undefined){
   console.log('truthy');
}
else{
   console.log('falsy'); // falsy
}

Special cases:

  • NaN doesn't equal to anything, not even itself
console.log(NaN == NaN); // false

SIDE NOTE:

Ever wondered why NaN is not equal to anything, including itself?
Well, there are 2 types of NaN - Quiet NaN (silently assigned) and Signalling NaN (throws an exception). Though, both of them are NaN values, their bits differ in internal representation, so it's possible you are comparing Quiet NaN with Signalling NaN. Hence, JavaScript refuses to compare NaN with anything, including itself.

Take a look at V8 Engines internal function JSEqualTyper (C++).The second if statement clearly states that if either side of == is a NaN, straightaway return false.

Type Typer::Visitor::JSEqualTyper(Type lhs, Type rhs, Typer* t) {
  if (lhs.IsNone() || rhs.IsNone()) return Type::None();
  if (lhs.Is(Type::NaN()) || rhs.Is(Type::NaN())) return t->singleton_false_;
  if (lhs.Is(Type::NullOrUndefined()) && rhs.Is(Type::NullOrUndefined())) {
    return t->singleton_true_;
  }
  if (lhs.Is(Type::Number()) && rhs.Is(Type::Number()) &&
      (lhs.Max() < rhs.Min() || lhs.Min() > rhs.Max())) {
    return t->singleton_false_;
  }
  if (lhs.IsSingleton() && rhs.Is(lhs)) {
    // Types are equal and are inhabited only by a single semantic value,
    // which is not NaN due to the earlier check.
    DCHECK(lhs.Is(rhs));
    return t->singleton_true_;
  }
  return Type::Boolean();
}
  • When applying == to null or undefined, numeric conversion does not happen. null equals only to null or undefined, and does not equal to anything else.
null == 0               // false, null is not converted to 0
null == null            // true
undefined == undefined  // true
null == undefined       // true

This is because null and undefined, both are evaluated as false in terms of boolean in JavaScript. Hence, we get the values implicitly converted to Booleans.

Boolean(null)       // false
Boolean(undefined)  // false

Useful Tip:

Type coercion can be confusing sometimes, since the decision of converting an operand to a particular type is entirely made by the javascript engine. In real-world applications, this can be scary. One operator that does not trigger implicit type coercion is ===, which is called the 'strict equality operator'.Hence always use the '===' operator.Hope this article was useful. Let me know if you guys have any questions. Happy Learning 😊😊...

Discussion (0)