Implicit Coercion β When JavaScript Decides
Implicit coercion happens automatically when JavaScript applies an operator to values of different types. The most famous cases involve the + operator:
// + with a string: everything becomes a string
console.log("5" + 3); // "53" β number coerced to string
console.log("5" + true); // "5true"
console.log("5" + null); // "5null"
console.log("5" + undefined); // "5undefined"
// - * / % trigger numeric coercion (they only do math)
console.log("5" - 3); // 2 β string coerced to number
console.log("5" * "2"); // 10
console.log("6" / "2"); // 3
console.log("hello" - 1); // NaN β "hello" can't become a number
// Comparison operators also coerce
console.log("5" > 3); // true β "5" becomes 5
console.log(null > 0); // false
console.log(null == 0); // false (null only == undefined)
console.log(null >= 0); // true β bizarre but true!
The + operator does double duty: addition for numbers, concatenation for strings. If either operand is a string, it concatenates. This is why "10" + 5 gives "105" but "10" - 5 gives 5. Always convert inputs explicitly before arithmetic.
Explicit Conversion
Explicit conversion means you intentionally convert a value using a built-in function. This is always preferred over relying on implicit coercion.
// String() β converts any value to string
console.log(String(42)); // "42"
console.log(String(true)); // "true"
console.log(String(null)); // "null"
console.log(String(undefined)); // "undefined"
console.log(String([1, 2, 3])); // "1,2,3"
// Number() β converts to number (strict: invalid = NaN)
console.log(Number("42")); // 42
console.log(Number("42.5")); // 42.5
console.log(Number("")); // 0
console.log(Number(true)); // 1
console.log(Number(false)); // 0
console.log(Number(null)); // 0
console.log(Number(undefined)); // NaN
console.log(Number("hello")); // NaN
// parseInt / parseFloat β more lenient
console.log(parseInt("42px")); // 42 (ignores trailing text)
console.log(parseFloat("3.14em")); // 3.14
// Boolean() β converts to boolean
console.log(Boolean(1)); // true
console.log(Boolean(0)); // false
console.log(Boolean("hello")); // true
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
Truthy and Falsy Values
In a boolean context (like an if condition), every value is either truthy or falsy. There are exactly 8 falsy values in JavaScript β everything else is truthy.
| Falsy Values | Notes |
|---|---|
false | The literal false |
0 | Zero |
-0 | Negative zero |
0n | BigInt zero |
"" | Empty string |
null | Intentional absence |
undefined | Unassigned |
NaN | Not a Number |
// All falsy β these if blocks do NOT run
if (false) console.log("false");
if (0) console.log("zero");
if ("") console.log("empty string");
if (null) console.log("null");
if (undefined) console.log("undefined");
if (NaN) console.log("NaN");
// Truthy surprises β these DO run!
if ("0") console.log('"0" is truthy!'); // non-empty string
if ("false") console.log('"false" is truthy!'); // non-empty string
if ([]) console.log('[] is truthy!'); // empty array
if ({}) console.log('{} is truthy!'); // empty object
if (-1) console.log('-1 is truthy!'); // any non-zero number
!! is a common idiom to explicitly convert any value to a boolean. !!null gives false, !!"hello" gives true. The first ! negates and coerces to boolean; the second ! negates back to get the correct boolean value.
The == vs === Coercion Table
Loose equality (==) applies type coercion using complex rules. Here are the most important cases to memorize:
// Surprising loose equality results
console.log(false == 0); // true
console.log(false == ""); // true
console.log(false == "0"); // false (!)
console.log(0 == ""); // true
console.log(0 == "0"); // true
console.log("" == "0"); // false
console.log(null == undefined); // true
console.log(null == 0); // false
console.log(undefined == 0); // false
console.log(NaN == NaN); // false (NaN is never equal to anything)
// The same with ===
console.log(false === 0); // false
console.log(null === undefined);// false
Converting Objects and Arrays to Strings
const arr = [1, 2, 3];
const obj = { name: "Alice", age: 30 };
// Array to string
console.log(String(arr)); // "1,2,3"
console.log(arr.toString()); // "1,2,3"
console.log(arr.join(" - ")); // "1 - 2 - 3"
// Object to string (not very useful)
console.log(String(obj)); // "[object Object]"
console.log(obj.toString()); // "[object Object]"
// JSON.stringify β proper object/array serialization
console.log(JSON.stringify(obj)); // '{"name":"Alice","age":30}'
console.log(JSON.stringify(obj, null, 2));
// Pretty-printed JSON with 2-space indent
// JSON.parse β convert JSON string back to object
const json = '{"name":"Bob","score":99}';
const parsed = JSON.parse(json);
console.log(parsed.name); // "Bob"
HTML form inputs always return strings. If you read a number from an input field and try to add it to another number without converting, you get concatenation: input.value + 5 gives "105" instead of 15. Always use Number(input.value) or +input.value first.
ποΈ Practical Exercise
- Convert the string
"123"to a number three ways:Number(),parseInt(), and the unary+operator (+"123"). - Use
!!to convert these to booleans and predict the result:0,"","0",[],null. - Demonstrate the
+operator coercion bug:"10" + 5 + 5vs10 + 5 + "5". - Use
JSON.stringify()to serialize an object andJSON.parse()to restore it. - Write a guard for a function that receives user input: convert it to a number and return
NaNfeedback if the conversion fails.
π₯ Challenge Exercise
Create a function safeAdd(a, b) that takes two values (which might be strings or numbers), converts both to numbers, checks that neither is NaN, and returns their sum. If either can't be converted, return an error message string like "Error: 'hello' is not a valid number". Test it with safeAdd("5", 3), safeAdd("5.5", "2.2"), safeAdd("abc", 10), and safeAdd(null, 5).
π Summary
- Implicit coercion: JavaScript auto-converts types. The
+operator concatenates if either operand is a string. - Explicit:
String(),Number(),Boolean(),parseInt(),parseFloat(). - Only 8 falsy values:
false,0,-0,0n,"",null,undefined,NaN. Everything else is truthy. - Empty arrays
[]and objects{}are truthy! - Use
===always. The==coercion rules are complex and surprising. - Use
JSON.stringify()to properly convert objects/arrays to strings.
Interview Questions
- What are the falsy values in JavaScript?
- What is the difference between implicit and explicit type conversion?
- Why does
"5" + 3give"53"but"5" - 3gives2? - Is an empty array
[]truthy or falsy? - How would you convert a value to boolean in JavaScript?
Frequently Asked Questions
This is one of the most confusing JavaScript quirks. [] is truthy in boolean contexts (like if ([])). But [] == false uses loose equality, which converts [] to a number (via string): [] β "" β 0, and false β 0, so 0 == 0 is true. This illustrates why using == is so dangerous β just use ===.
The unary + operator converts a value to a number, just like Number(): +"42" gives 42, +true gives 1, +"" gives 0, +"hello" gives NaN. It's a compact shorthand, but Number() is more readable β use whichever your team prefers.
No. JSON.stringify cannot serialize: undefined (omitted from objects, becomes null in arrays), functions (silently omitted), Symbol values, circular references (throws an error), BigInt (throws a TypeError), and special objects like Date (converted to ISO string). For complex serialization needs, use a library like superjson.
Use value == null β this is one of the few good uses of loose equality. It returns true for both null and undefined but false for everything else (including 0, "", and false). Alternatively, use value === null || value === undefined for explicit clarity.
Use !Number.isNaN(Number(str)) && str.trim() !== "". The str.trim() !== "" check is necessary because Number("") and Number(" ") both return 0 instead of NaN. Or use a regex: /^-?\d+(\.\d+)?$/.test(str) for basic integer/float validation.