JavaScript Type Conversion
Type conversion is the most error-prone part of JavaScript. A number typed into a form? Turns out it's a string! "5" + 3 gives "53" instead of 8 — every beginner encounters these "surprises."
📖 Summary
Why Type Conversion Matters
HTML form input values are always strings, even with type="number". You must convert types before doing arithmetic, or the results will be unpredictable.
<div id="demo"></div>
<script>
const input = "42";
const result = input + 8;
document.getElementById("demo").textContent = 'input + 8 = ' + result + ' (type: ' + typeof result + ')';
</script>
Explicit Conversion: String(), Number(), Boolean()
Calling conversion functions explicitly makes your intent clear — this is the recommended approach:
| Conversion | Syntax | Example |
|---|---|---|
| To string | String(value) |
String(123) → "123" |
| To number | Number(value) |
Number("42") → 42 |
| To boolean | Boolean(value) |
Boolean(0) → false |
Implicit Conversion: + Concatenation and == Comparison
The JavaScript engine performs automatic type conversion that often produces unexpected results:
+operator: if either side is a string, the other gets converted to a string for concatenation==comparison: converts types before comparing, with complex rules that are hard to remember
<script>
console.log("5" + 3); // "53" (number 3 converted to string)
console.log("5" - 3); // 2 (string "5" converted to number)
console.log("" == 0); // true (empty string converted to 0)
console.log(null == undefined); // true
</script>
Common Pitfalls
These are high-frequency gotchas in interviews and real-world development:
<script>
console.log("5" + 3); // "53" + concatenates when it sees a string
console.log("5" - 3); // 2 - only does subtraction, converts string to number
console.log("5" * 3); // 15 * also converts to number
console.log(true + 1); // 2 true converts to 1
console.log(false + 1); // 1 false converts to 0
console.log("" + 0); // "0" number converts to string
console.log("" == 0); // true both sides convert to numbers
console.log(null == 0); // false null only loosely equals undefined
console.log("0" == false); // true both convert to 0
</script>
Remember this rule: + concatenates when it encounters a string; all other arithmetic operators convert strings to numbers.
Number() Conversion Rules
| Input | Result |
|---|---|
Number("42") |
42 |
Number("3.14") |
3.14 |
Number("") |
0 (empty string → 0, classic gotcha) |
Number(" ") |
0 (whitespace-only string → 0) |
Number("42px") |
NaN (contains non-numeric characters → NaN) |
Number(true) |
1 |
Number(false) |
0 |
Number(null) |
0 |
Number(undefined) |
NaN |
Empty string converts to 0 instead of NaN — this design has tripped up countless developers.
Boolean() Conversion Rules (Falsy Values)
These 6 values convert to false and are called falsy values. Everything else is true:
<script>
console.log(Boolean(false)); // false
console.log(Boolean(0)); // false
console.log(Boolean(-0)); // false
console.log(Boolean("")); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN)); // false
</script>
Note: Boolean("0") is true, and Boolean([]) is also true — non-empty strings and non-empty arrays are truthy.
=== Strict Equality vs == Loose Equality
| Operator | Behavior | Recommendation |
|---|---|---|
=== |
Returns false if types differ | Always use |
== |
Converts types before comparing | Avoid |
<script>
console.log(5 === "5"); // false (different types)
console.log(5 == "5"); // true (string converted to number first)
console.log(null === undefined); // false
console.log(null == undefined); // true
</script>
Iron rule: always use ===, never ==. Unless you know exactly what you're doing (e.g., value == null can match both null and undefined).
Example: Type Conversion Comparison
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Type Conversion</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 700px; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: left; }
th { background: #4a90d9; color: #fff; }
.surprise { color: #d9534f; font-weight: bold; }
.normal { color: #5cb85c; }
tr:nth-child(even) { background: #f9f9f9; }
</style>
</head>
<body>
<h2>Implicit Conversion Results at a Glance</h2>
<div id="output"></div>
<script>
const tests = [
['"5" + 3', "5" + 3, '"53"', "String concatenation", true],
['"5" - 3', "5" - 3, "2", "String to number", false],
['"5" * 3', "5" * 3, "15", "String to number", false],
['true + 1', true + 1, "2", "true→1", false],
['false + 1', false + 1, "1", "false→0", false],
['"" + 0', "" + 0, '"0"', "Number to string", true],
['"" == 0', "" == 0, "true", "Both sides convert to numbers", true],
['"0" == false', "0" == false, "true", "Both convert to 0", true],
['null == 0', null == 0, "false", "null doesn't equal 0", false],
['5 === "5"', 5 === "5", "false", "Different types", false],
];
document.getElementById("output").innerHTML = `
<table>
<tr><th>Expression</th><th>Actual Result</th><th>Type</th><th>Explanation</th><th>Counter-intuitive?</th></tr>
${tests.map(t => `<tr>
<td><code>${t[0]}</code></td>
<td class="${t[4] ? 'surprise' : 'normal'}">${t[2]}</td>
<td>${typeof t[1]}</td>
<td>${t[3]}</td>
<td>${t[4] ? "Yes" : "No"}</td>
</tr>`).join("")}
</table>
`;
</script>
</body>
</html>
Example: Boolean() Falsy Values Quick Reference
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Boolean Conversion</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; margin: 16px 0; }
.card { padding: 16px; border-radius: 8px; text-align: center; font-weight: bold; }
.falsy { background: #fff0f0; border: 2px solid #d9534f; color: #d9534f; }
.truthy { background: #f0fff0; border: 2px solid #5cb85c; color: #5cb85c; }
.card code { display: block; font-size: 18px; margin-bottom: 4px; }
.card span { font-size: 13px; opacity: 0.8; }
.note { background: #fff8e1; padding: 12px; border-radius: 6px; border-left: 4px solid #f0ad4e; margin: 16px 0; }
</style>
</head>
<body>
<h2>Boolean() Conversion Results</h2>
<div id="output"></div>
<script>
const values = [
[false, "false"],
[0, "0"],
["", '""'],
[null, "null"],
[undefined, "undefined"],
[NaN, "NaN"],
[true, "true"],
[1, "1"],
["0", '"0"'],
["false", '"false"'],
[[], "[]"],
[{}], "{}"
];
const cards = values.map(([val, label]) => {
const result = Boolean(val);
const display = label || String(val);
return `<div class="card ${result ? 'truthy' : 'falsy'}">
<code>${display}</code>
<span>${result}</span>
</div>`;
});
document.getElementById("output").innerHTML = `
<div class="grid">${cards.join("")}</div>
<div class="note">
<code>"0"</code> is truthy! <code>[]</code> is also truthy! Only those 7 falsy values convert to false.
</div>
`;
</script>
</body>
</html>
Example: Form Input Calculation
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Form Calculation</title>
<style>
body { font-family: sans-serif; padding: 20px; }
.calc { max-width: 400px; margin: 16px auto; padding: 20px; background: #f9f9f9; border-radius: 8px; }
.row { margin: 12px 0; }
label { display: inline-block; width: 80px; }
input { padding: 8px 12px; font-size: 16px; border: 2px solid #ccc; border-radius: 6px; width: 120px; }
button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; background: #4a90d9; color: #fff; margin-top: 8px; }
button:hover { background: #357abd; }
.result { margin-top: 16px; padding: 12px; border-radius: 6px; font-size: 18px; font-weight: bold; }
.right { background: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.wrong { background: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
</style>
</head>
<body>
<h2 style="text-align:center;">Form Input Calculation (Type Conversion in Practice)</h2>
<div class="calc">
<div class="row">
<label>Price:</label>
<input type="number" id="price" value="12.5" />
</div>
<div class="row">
<label>Quantity:</label>
<input type="number" id="qty" value="3" />
</div>
<div style="text-align:center;">
<button id="calcWrong">Calculate without conversion (wrong)</button>
<button id="calcRight" style="background:#5cb85c;">Calculate with Number()</button>
</div>
<div class="result" id="result"></div>
</div>
<script>
document.getElementById("calcWrong").addEventListener("click", function() {
const price = document.getElementById("price").value;
const qty = document.getElementById("qty").value;
const total = price * qty;
document.getElementById("result").className = "result wrong";
document.getElementById("result").innerHTML =
`Without conversion: "${price}" * "${qty}" = ${total}<br>` +
`(${typeof price} * ${typeof qty} = ${typeof total})<br>` +
`It works by luck because * implicitly converts, but + would break!`;
});
document.getElementById("calcRight").addEventListener("click", function() {
const price = Number(document.getElementById("price").value);
const qty = Number(document.getElementById("qty").value);
if (isNaN(price) || isNaN(qty)) {
document.getElementById("result").className = "result wrong";
document.getElementById("result").textContent = "Invalid input!";
return;
}
const total = price * qty;
document.getElementById("result").className = "result right";
document.getElementById("result").innerHTML =
`With explicit conversion: ${price} × ${qty} = ${total.toFixed(2)}<br>` +
`(${typeof price} × ${typeof qty} = ${typeof total})`;
});
</script>
</body>
</html>
Example: === vs == Comparison
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Strict vs Loose Equality</title>
<style>
body { font-family: sans-serif; padding: 20px; }
table { border-collapse: collapse; margin: 16px 0; width: 100%; max-width: 600px; }
td, th { border: 1px solid #ddd; padding: 10px 14px; text-align: center; }
th { background: #4a90d9; color: #fff; }
.same { background: #d4edda; }
.diff { background: #f8d7da; }
.recommend { background: #fff8e1; padding: 16px; border-radius: 8px; border-left: 4px solid #f0ad4e; margin: 16px 0; max-width: 600px; }
</style>
</head>
<body>
<h2>=== Strict Equality vs == Loose Equality</h2>
<div id="output"></div>
<script>
const comparisons = [
["5", 5],
[0, false],
[0, ""],
["0", false],
[null, undefined],
[null, 0],
[null, false],
[NaN, NaN],
];
const rows = comparisons.map(([a, b]) => {
const loose = a == b;
const strict = a === b;
const aStr = JSON.stringify(a);
const bStr = JSON.stringify(b);
return `<tr>
<td><code>${aStr}</code> vs <code>${bStr}</code></td>
<td class="${loose ? 'same' : 'diff'}">${loose}</td>
<td class="${strict ? 'same' : 'diff'}">${strict}</td>
</tr>`;
});
document.getElementById("output").innerHTML = `
<table>
<tr><th>Comparison</th><th>== Result</th><th>=== Result</th></tr>
${rows.join("")}
</table>
<div class="recommend">
<strong>Iron rule</strong>: Always use <code>===</code>. The conversion rules for <code>==</code> are too complex — except for the specific case of <code>value == null</code> (which matches both null and undefined), don't use it.
</div>
`;
</script>
</body>
</html>
❓ FAQ
"5" - 3 equal 2 instead of throwing an error?- operator only performs math, so JavaScript automatically converts the string to a number. Only the + operator is ambiguous (it can be addition or concatenation), so it concatenates when it sees a string. All other operators (-, *, /, %) always convert to numbers.Number("") return 0 instead of NaN?Number(input.value) returns 0 for empty fields instead of NaN, which can mislead downstream logic. Use input.value.trim() === "" to check for empty values first.null == 0 false?== comparisons, null only converts to/from undefined — it doesn't convert to numbers. So null == 0 is false, but null == undefined is true. This is a special case defined by the specification.📝 Exercises
- Without running the code, predict the results of these expressions:
"10" + 5,"10" - 5,"10" * "2",true + true,Boolean("false"),"1" == 1,"1" === 1. Then write code to verify. - Write a
calculateBMI(weight, height)function where parameters come from form inputs (strings). The function should convert types internally before calculating BMI, and handle empty or invalid inputs (return an error message). - Write a
strictCompare(a, b)function that uses only===for comparison. If the types differ, return"Different types, cannot compare"directly; if types match, return the comparison result.



