---
title: "04 — Type Casting — Coercion You Ask For vs Coercion That Bites You"
uid: type-casting
tags: ["conversion", "roadmap:javascript", "coercion", "implicit", "explicit", "type-casting", "javascript"]
excerpt: "Two flavors of conversion — explicit (you wrote the cast) and implicit (the operator coerced for you) — and most type bugs come from pretending implicit isn't happening."
date: 2026-08-13T03:28:07+0000
source: https://www.aveshina.my.id/en/blog/type-casting
---

"The language doing weird things with types" was how I filed coercion, and it kept me from seeing the two clean flavors underneath. The idea that everything else hangs off: **there are exactly two flavors of conversion, explicit (you wrote the cast) and implicit (the operator coerced for you), and most type bugs come from pretending the implicit kind isn't happening.** [1]

The framing that finally landed is the split between *type conversion* and *type coercion* — same mechanism, different authorship [2]. **Explicit conversion** (also called type casting) is when I write the conversion: Number("42"), String(5), Boolean(x). I asked for it; it's in the code. **Implicit coercion** is when an operator or statement does it for me, because JavaScript is loosely typed and will try to make the operands fit: "5" + 3 becoming "53", "5" - 3 becoming 2. Both produce the same kind of result; the difference is who decided.

## Implicit coercion: where the surprises live

JavaScript is loosely typed, so operators automatically convert a value to the type they expect [3]. The two operators that bite most often:

- **+ with a string operand** — if either side is a string, + does *string concatenation*, not arithmetic. "3" + 4 is "34". The number got coerced to a string.
- **-, `*, /** — these only do math, so both operands get coerced to numbers. "3" - 4 is -1, "6" * "7" is 42`.

The asymmetry is the trap: + is overloaded, the others aren't. So "5" + 3 and "5" - 3 give wildly different results ("53" vs 2) even though they look symmetric in the code.

The other classic is equality. The loose == operator coerces before comparing, which is why "" == 0 is true, "0" == false is true, and null == undefined is true but null == 0 is false [4]. There is an algorithm (the Abstract Equality Comparison) defining all of this, but memorizing it is a waste — the rule is just *don't use ==*; use === and coerce explicitly.

Falsy values round out the implicit story. In a boolean context (if, &&, ||), these coerce to false: 0, "", null, undefined, NaN, and (of course) false. Everything else is truthy — including "0", [], and {}, which is the next layer of surprises.

## Explicit casting: asking on purpose

Explicit conversion is the cure for all of the above — convert deliberately, then the operators do what you expect [5]. The toolkit:

```
// To number
Number("42");        // 42
parseInt("42px", 10); // 42 — parses a leading integer
parseFloat("3.14");   // 3.14
+"42";                // 42 — unary plus, the shortest cast

// To string
String(42);           // "42"
(42).toString();      // "42"
`${42}`;              // "42" — template literal

// To boolean
Boolean("");          // false
!!x;                  // shortest boolean cast — double negation
```

parseInt is worth a note on its own: it parses from the *start* of a string and stops at the first non-numeric character, so parseInt("42px") is 42. Always pass the radix (the 10) — without it, leading-zero strings like "08" used to be parsed as octal, and while modern JS defaults to decimal, the explicit radix documents intent.

## How I use this

The discipline is simple, and it's the one I'd hand to anyone: **never rely on implicit coercion; always cast explicitly; always compare with ===.** When I need a number from user input, Number(value) or parseInt(value, 10) up front. When I need a string, String(value) or a template literal. When I need a boolean, Boolean(value) or !!. Implicit coercion still happens inside if conditions (and that's fine — truthy/falsy is the one place it's idiomatic), but I never let it sneak across an operator. The payoff is that "why did '5' + 3 give '53'?" simply stops being a category of bug I write.

## References

[1] I. Kantor, "Type Conversions," The Modern JavaScript Tutorial, 2024. [Online]. Available: [https://javascript.info/type-conversions](https://javascript.info/type-conversions)

[2] Mozilla, "Type Conversion and Type Coercion (Glossary)," MDN Web Docs, 2024. [Online]. Available: [https://developer.mozilla.org/en-US/docs/Glossary/Type_Conversion](https://developer.mozilla.org/en-US/docs/Glossary/Type_Conversion)

[3] promisetochi, "What you need to know about JavaScript's Implicit Coercion," dev.to, 2022. [Online]. Available: [https://dev.to/promisetochi/what-you-need-to-know-about-javascripts-implicit-coercion-e23](https://dev.to/promisetochi/what-you-need-to-know-about-javascripts-implicit-coercion-e23)

[4] Mozilla, "Equality comparisons and sameness," MDN Web Docs, 2024. [Online]. Available: [https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness)

[5] c-sharpcorner, "Type Conversions in JavaScript," 2022. [Online]. Available: [https://www.c-sharpcorner.com/article/type-conversions-in-javascript/](https://www.c-sharpcorner.com/article/type-conversions-in-javascript/)

```quiz
Q: `"5" + 3` returns `"53"`, but `"5" - 3` returns `2`. Why the difference?
- + is overloaded: if either operand is a string it concatenates. The arithmetic operators (- * /) only do math, so they coerce both operands to numbers.
- It's a parser inconsistency that ES2020 fixed
correct: 0
explain: The + operator does string concatenation when either side is a string, coercing the number to a string. -, *, and / have no string meaning, so they coerce both operands to numbers.

Q: `null == undefined` is `true`, but `null == 0` is `false`. What's going on?
- The loose == operator follows the Abstract Equality Comparison algorithm; null and undefined are a special pair that are loosely equal only to each other
- null is a number zero in disguise
correct: 0
explain: The == algorithm has a special rule: null and undefined are equal to each other and to nothing else (without further coercion). The fix is to use ===, which never coerces.

Q: Which value is truthy when most people expect falsy?
- "" (empty string)
- "0" (a string with the character zero)
correct: 1
explain: "0" is a non-empty string, and all non-empty strings are truthy. Only "", 0, null, undefined, NaN, and false are falsy.

Q: What is the shortest idiomatic way to explicitly cast a value to a boolean?
- Boolean(value)
- !!value
correct: 1
explain: Both work; !! (double negation) is the common shorthand. The first ! coerces to boolean and negates, the second negates back to the original truthiness.

Q: Why always pass the radix to parseInt, e.g. parseInt("08", 10)?
- Without it, legacy engines could parse leading-zero strings as octal; passing 10 documents the intent and guarantees decimal
- parseInt ignores the second argument
correct: 0
explain: Historically, parseInt without a radix inferred the base from a leading zero (octal). Modern JS defaults to decimal, but passing 10 removes ambiguity and documents intent.
```
