Module 2 · Data Types & Control Flow Deep Dive ⏱ 18 min

Type Coercion & Explicit Conversion

By the end of this lesson you will be able to:
  • Predict what implicit coercion does to mixed types
  • Convert values explicitly between string, number, and boolean
  • Handle NaN safely when a conversion fails

JavaScript is a dynamically typed language: a variable can hold a number one moment and a string the next, and the language will often convert between the two on your behalf to keep the program running rather than crashing. That automatic conversion is called implicit coercion, and it is a genuine convenience — most of the time. The cost is that the same operator can quietly change the meaning of your data, turning a number into text or text into a number without ever asking.

The classic surprise is the + operator. Because + is overloaded to do both arithmetic and string joining, JavaScript must guess which one you meant, and its rule is simple but easy to forget: if either operand is a string, both sides become strings. That single rule is responsible for an entire genre of beginner bugs — a form total that comes out as "1020" instead of 30 — so it is worth understanding the mechanism once, rather than memorising outcomes case by case.

flowchart LR
  A["Implicit coercion"] --> B["JS converts types for you"]
  C["Explicit conversion"] --> D["You call Number, String, Boolean"]
  style B fill:#e0900b,color:#fff
  style D fill:#e0900b,color:#fff
Implicit coercion happens automatically; explicit conversion is a call you make on purpose.

Why + and - behave differently

Watch the same two values under two operators:

"5" + 1   // "51" — the 1 became a string
"5" - 1   // 4    — the "5" became a number

The + operator sees a string on the left and chooses concatenation, so the number 1 is coerced into the string "1" and glued onto the end. The - operator has no meaning for strings — you cannot subtract one piece of text from another — so JavaScript coerces the other way and turns "5" into the number 5. The same logic explains why "3" * "2" becomes 6: arithmetic operators always force numbers. When the result matters, do not rely on remembering which way each operator coerces — convert explicitly, and the ambiguity vanishes.

Convert explicitly with Number, String, and Boolean. Press Run.
console.log(Number("42"));
console.log(Number("abc"));
console.log(String(42));
console.log(Boolean(0));
console.log(Boolean("hi"));
flowchart TD
  Q{"Either side a string?"} -- Yes --> S["coerce both to strings<br/>and concatenate"]
  Q -- No --> N["coerce both to numbers<br/>and add"]
  style S fill:#b45309,color:#fff
  style N fill:#1e7f3d,color:#fff
The + operator: if either side is a string, both become strings and join; otherwise both become numbers and add.

Converting on purpose

When you want certainty rather than a guess, take control with the three wrapper functions: Number(value), String(value), and Boolean(value). Each tries to produce a value of its own type and is unambiguous about what it returns, so there is no operator overloading left to second-guess.

Number("42") gives the number 42; String(42) gives the string "42". The two are mirror images, and reaching for the one that points the direction you need removes the ambiguity entirely. Boolean deserves a moment on its own, because it does far more than check for true and false: it asks whether a value is truthy or falsy. JavaScript treats exactly six values as falsy — 0, "", NaN, null, undefined, and false itself — and absolutely everything else as truthy, including the genuinely surprising "0" (it is a non-empty string) and [] (an empty array).

When a conversion fails: NaN

Not every value can become a number. Number("abc") has no sensible numeric answer, so JavaScript returns NaN — short for 'Not a Number', though its type is, absurdly, "number". NaN is the language's way of saying 'I tried, and the result is not a real number'.

NaN has two properties that make it dangerous in real code. First, it is contagious: any arithmetic that touches NaN produces more NaN, so a single bad parse can poison a long calculation without raising any error at all. Second, NaN is the only value in the entire language that is not equal to itselfNaN === NaN is false. That means you can never detect it with ===; you must use the dedicated test Number.isNaN(value), which exists precisely because the ordinary comparison is broken.

NaN is contagious and never equals itself. Number.isNaN is the only reliable test.
const price = Number("free");
console.log(price);
console.log(price + 10);
console.log(price === NaN);
console.log(Number.isNaN(price));
flowchart LR
  V["any value"] --> B{"Boolean(value)"}
  B -- "0, '', NaN, null,<br/>undefined, false" --> F["false"]
  B -- "everything else" --> T["true"]
  style F fill:#b91c1c,color:#fff
  style T fill:#1e7f3d,color:#fff
Boolean() maps exactly six falsy values to false; absolutely everything else becomes true.
The truthiness of each value. Note that '0' and [] are both truthy.
console.log(Boolean(0));
console.log(Boolean(""));
console.log(Boolean("0"));
console.log(Boolean(1));
console.log(Boolean([]));
console.log(Boolean("false"));

Convert at the boundaries

A robust habit is to convert at the edges of your program — the moment untrusted data arrives, whether from a user, a form field, or a file — and then work in one consistent type everywhere inside. Parse every input with Number once, decide what to do with NaN right there at the door, and from that point on your code can safely assume it is holding real numbers. Coercion becomes something that happens once on entry, not a surprise hiding inside every +.

This is the real difference between a program that mostly works and one you can reason about. Spend a single line converting explicitly, and you buy back the certainty that the rest of the calculation is doing exactly what it looks like it is doing.

Exercise

What does this print? Remember: + concatenates when a string is involved, but - always forces numbers.

console.log("5" + 1);
console.log("5" - 1);
Exercise

Write parsePrice(str) that converts a string to a number, and returns 0 when the string cannot be parsed (that is, when the result is NaN).

function parsePrice(str) {
  // return the number, or 0 if it is NaN
}
Exercise

What does this print? NaN is the only value in JavaScript that is not equal to itself — so even comparing it to itself is false.

const a = Number("abc");
console.log(a);
console.log(a === a);
console.log(a === NaN);
Exercise

This safeSquareRoot(n) is meant to return the square root of n when it is a valid number, and null when it is not. But the author tested for NaN with === NaN, which is never true — so invalid input sails straight through and produces a meaningless NaN result. Fix the test so bad input correctly returns null.

function safeSquareRoot(n) {
  const num = Number(n);
  if (num === NaN) {
    return null;
  }
  return Math.sqrt(num);
}
Exercise

Write sumInputs(inputs) where inputs is an array of strings such as ["10", "20", "oops"]. Convert each to a number, ignore any that become NaN, and return the total of the rest. So sumInputs(["10", "20", "oops"]) returns 30.

function sumInputs(inputs) {
  // convert each to a number, skip NaN, return the sum
}

Recap

  • Implicit coercion converts types automatically; explicit conversion is a call you make with Number, String, or Boolean.
  • The + operator concatenates if either side is a string; -, *, and / always force numbers.
  • A failed numeric conversion produces NaN — contagious in arithmetic, and never equal to itself.
  • Detect NaN with Number.isNaN, never with === NaN.
  • Boolean maps exactly six falsy values to false; everything else is true, including "0" and [].

Next you will meet truthy and falsy values in full, and see how if, &&, and || lean on them to make decisions.

Checkpoint quiz

What does "5" - 1 evaluate to?

Which built-in converts a value to a number explicitly?

Why does value === NaN never detect NaN?

Go deeper — technical resources