Type coercion

Data type conversion

In general, operands in an operation need to be of the same data type before the operation can be performed. For instance, comparing a string to a boolean needs some data type conversion from one data type to another, to make them of the same type. Also other kinds of expressions may need values of a specific type and type conversion when the provided values are of the wrong data type. Type conversion can be implicit (automatic) or explicit.

Type coercion

Implicit type conversion or implicit type casting is often called type coercion or simply coercion. With type coercion, data types are automatically converted to appropriate data types at run time. In contrast to strongly typed languages, weakly typed (loosely typed) languages, such as JavaScript, involve a lot of type coercion. This can be convenient, because "errors" in the code are "fixed" at run time, but it can also be quite a curse, because of all the language specific coercion rules and a potential "messy" code.

So, at run time, operands of different types are coerced into appropriate types for the operation. Also functions and methods may require arguments of a specific type and JS will try to coerce them if they are specified in a different type. For instance calling the window.alert(true) method will automatically convert the specified argument true (data type boolean) to a string and displays it as a message string "true".

Arithmetic/numerical contexts

In arithmetic/numerical contexts values are generally coerced into numbers.

If a string content does not match the format of a valid number, the result of a number conversion is NaN. NaN stands for "Not a Number" and is a value of data type "number". Preceding and succeeding white spaces in a string are ignored in a number conversion, but white spaces enclosed in the string are not, and generally result in NaN.


console.log(NaN * 5); // logs: NaN
console.log("foo" * "bar"); // logs: NaN
console.log("ten" * 2); // logs: NaN
console.log("10" / "2"); // logs: 5
console.log("  10  " % "3"); // logs: 1
console.log("1   0" % "3"); // logs: NaN

console.log(Math.pow("2","3")); // logs: 8 // 2 to the power 3 equals 8.
console.log(Math.floor("1   0" / "3")); // logs: NaN

BTW: Here a list of Math methods of this built-in object.

In a implicit number conversion, in arithmetic/numerical contexts, null coerces into 0, undefined coerces into NaN, the empty string ("", including a string with only spaces) coerces into 0, boolean true coerces into 1 and false coerces into 0.


console.log('' - "2"); // logs: -2 // after coercion: 0 - 2
console.log('' * null); // logs: 0 // after coercion: 0 * 0
console.log(Math.PI * null); // logs: 0 // after coercion: 3.14... * 0
console.log(undefined - 2); // logs: NaN // after coercion: NaN - 2
console.log(("3" || true)**2 ); // logs: 9 // ("3" || true)**2 => "3"**2 => 3**2 = 9.
console.log(false - 1); // logs: -1 // after coercion: 0 - 1
console.log(true - true); // logs: 0 // after coercion: 1 - 1
console.log(true * 2); // logs: 2 // after coercion: 1 * 2
console.log(0 * "null"); // logs: NaN // after coercion: 0 * NaN

console.log(Math.pow(null,null)); // logs: 1 // after coercion: Math.pow(0,0))

An exception to this all is when operands are added using the addition operator (a + b). In this case, the operation could be interpreted as an addition of numbers or a concatenation of strings. With the addition operator, operands are coerced into strings and concatenated if at least one of both operands is a string. This means that the operands are only mathematically added if:

If mathematically added and one of the operands is undefined, the result will always be NaN because undefined coerces to NaN in a numerical context.


let numString = "1";
console.log(numString -= "2"); // logs -1 // A number.
console.log(numString += "2"); // logs "-12" // String concatenation.

console.log(10 + 2); // logs: 12 // A number.
console.log("10" + "2"); // logs: '102' // String concatenation.
console.log('2' + 2); // logs: '22' // String concatenation.
console.log('' + 2); // logs: '2' // String concatenation.
console.log('  ' + 2); // logs: '  2' // String concatenation.
console.log(null + 2); // logs: 2 // A number.
console.log(true + 2); // logs: 3 // A number.
console.log(true + "2"); // logs: true2 // String concatenation. 
console.log('' + null); // logs: 'null' // String concatenation of '' and 'null'.
console.log(null + false); // logs: 0
console.log(true + true); // logs: 2
console.log(true + null); // logs: 1
console.log(NaN + 5); // logs: NaN
console.log(NaN + true); // logs: NaN
let someVar; // automatically initialized with the value "undefined"
console.log(someVar + 2); // logs: NaN
console.log(someVar + null); // logs: NaN
console.log(someVar + ""); // logs: "undefined" // String concatenation.

console.log(7 + 3 + " years"); // logs: "10 years" // First 7 + 3 = 10, then "10" + " years" = "10 years".
console.log(7 + '3' + 10); // logs: "7310" // First "7" + "3" = "73", then "73" + "10" = "7310".
console.log("Result: " + 7 + 3); // logs: "Result: 73" // String concatenation of "Result: ", "7" and "3".
console.log("Result: " + (7 + 3)); // logs: "Result: 10" // String concatenation of "Result: " and "10".

If we want "2" + 2 to add as numbers, instead of concatenating strings, we need to explicitly convert "2" to a number. More about this in the chapter about explicit type conversion.


console.log(+"2" + 2); // logs: 4 // Explicit conversion of "2" to number 2 by using the unary plus operator.

Comparison operations

Comparison operations coerce operands in a similar way as operations in arithmetic/numerical contexts do, although the result is a boolean and although there are a number of exceptions.

Earlier we saw that the non-strict equality operators (== and !=) coerce one or both of the operands of different types into an appropriate type for the comparison, generally converting to numbers and comparing numerically. Only when both operands are of the same data type, no coercion will happen.

The strict equality operators (=== and !==) never coerce type conversion because they also evaluate the equality of types in the comparison.


console.log("foo" == "bar"); // logs: false // no coercion
console.log("foo" == "foo"); // logs: true // no coercion
console.log(true == false); // logs: false // no coercion
console.log("" == "0"); // logs: false // no coercion

console.log(1 == false); // logs: false // false coerced to 0 before compared numerically
console.log("2" == 2); // logs: true // "2" coerced to number 2 before compared numerically
console.log("2" === 2); // logs: false // Different types.
console.log(" 2 " == 2); // logs: true // after coercion: 2 == 2

console.log("" == 0); // logs: true // after coercion: 0 == 0
console.log(" " == 0); // logs: true // after coercion: 0 == 0

console.log(true == "hello"); // logs: false // after coercion: 1 == NaN
console.log(false == "hello"); // logs: false // after coercion: 0 == NaN
console.log(false == "0"); // logs: true // after coercion: 0 == 0

console.log("1 3" == 13); // logs: false // after coercion: NaN == 13
console.log(NaN == 2); // logs: false

Also comparison operations greater than (or equal to) and less than (or equal to) coerce operands in a similar way as operations in arithmetic/numerical contexts do. One of the exceptions is that if both operands are strings, they are compared alphabetically instead of coerced to numbers.


console.log("a" >= 1); // logs: false // NaN is not equal to or greater than 1.
console.log(false >= 0); // logs: true
console.log(true <= "2"); // logs: true
console.log(" 2 " > 1); // logs: true
console.log(null <= 0); // logs: true
console.log(null <= "0"); // logs: true
console.log(true > false); // logs: true
console.log(true >= true); // logs: true
console.log(undefined <= 3); // logs: false // NaN is not equal to or less than 3.
console.log(undefined <= null); // logs: false // NaN is not equal to or less than 0.
console.log(NaN <= 3); // logs: false // NaN is not less than or equal to 3.
console.log(NaN > 3); // logs: false
console.log(true > null); // logs: true
console.log(false >= ""); // logs: true
console.log(NaN <= false); // logs: false

console.log('Alice' < "Beth"); // logs: true // compared alphabetically (case sensitive!).
console.log("1" < "2"); // logs: true
console.log('2' < '123'); // logs: false // compared alphabetically 2 is greater than 1.

NaN and null

Comparison operations where both operands are falsy (false, 0, "", null, undefined or NaN) may lead to very confusing results.

In comparison operations greater than (or equal to) and less than (or equal to), NaN and undefined always compare to themselves or each other as false. In non-strict equality operations not only comparing NaN with itself results in an unexpected false, also comparing null results in weirdness.


console.log(NaN <= NaN); // logs: false
console.log(NaN > NaN); // logs: false
console.log(undefined <= NaN); // logs: false
console.log(undefined <= undefined); // logs: false

console.log(undefined == undefined); // logs: true

console.log(null >= 0); // logs: true
console.log(null == 0); // logs: false // apparently this does NOT coerce into 0 == 0

console.log("" == null); // logs: false // apparently this does NOT coerce into 0 == 0
console.log(false == null); // logs: false // apparently this does NOT coerce into 0 == 0

console.log(null >= undefined); // logs: false
console.log(null == undefined); // logs: true // apparently this does NOT coerce into 0 == NaN

console.log(NaN == NaN); // logs: false
console.log(NaN === NaN); // logs: false

NaN does not even strictly equal itself: console.log(NaN === NaN) returns false. You will need the Number.isNaN() method to determine whether a value strictly equals NaN.


let someVar = 0/0;
console.log(someVar); // logs: NaN
console.log(someVar === NaN); // logs: false
console.log(Number.isNaN(someVar)); // logs: true

someVar = "NaN";
console.log(Number.isNaN(someVar)); // logs: false // "NaN" is a string, not the value NaN
console.log(isNaN(someVar)); // returns: true. // the 'old' global function is used here

It is generally safer to use the strict equality operators (=== and !==) instead of the non-strict ones. The strict equality operators never coerce into type conversion. Only strictly comparing against NaN may need some special treatment, as mentioned above.

Object coercion

Object operands need to be coerced into primitives before an operation can be evaluated. This is done by methods valueOf() or toString() or by means of Symbol.toPrimitive. More about this in chapter Object to primitive conversion.


let stringPrimitive = 'hello';
let stringObject = new String(stringPrimitive);

console.log(stringPrimitive == stringObject); // logs: true // stringObject is coerced into a primitive value through its method valueOf(). 
console.log(stringPrimitive === stringObject); // logs: false // Different types ("string" and "object").