---
title: 'Type Conversion and Coercion'
source: 'https://academia.sh/en/courses/javascript-fundamentals/type-conversion-and-coercion'
course: 'JavaScript Fundamentals'
language: en
updated: '2026-08-23T07:00:59+00:00'
license: 'CC BY-SA 4.0'
---

# Type Conversion and Coercion

The distinction between explicit conversion and implicit coercion; the procedures for converting to number, to string, and to primitive value; the two faces of the addition operator, and reading measurement values from text.

The previous lesson showed that mixing the two number types in arithmetic operators is
rejected. This is not the language's general rule; it is the exception. In the rest of
the language, types are converted on their own, and the result depends on the order in
which the conversion is done.

The Type Conversion lesson in the Programming Fundamentals course established the
distinction between explicit and implicit conversion. This lesson applies that
distinction to JavaScript and defines the steps of implicit conversion — the behavior
that will be called **coercion** in this course. The goal is not to memorize results, but
to be able to trace the procedure that produces the result.

## Three Target Types

Coercion is always toward one of three targets: number, string, or boolean. The standard
defines three abstract operations for these. This lesson covers the first two; converting
to boolean is the subject of the Truthy and Falsy Values lesson.

The to-number conversion procedure gives a fixed rule for each primitive type:

```js
console.log(Number("21.5"));
console.log(Number(""));
console.log(Number("   "));
console.log(Number("21.5 C"));
console.log(Number(null));
console.log(Number(undefined));
console.log(Number(true), Number(false));
```

```
21.5
0
0
NaN
0
NaN
1 0
```

Four lines need attention. An empty string and a string containing only whitespace give
zero — the text "containing no number" results in being counted as zero. Text that starts
with a number and continues with a letter gives `NaN`; the procedure requires the
**entire** text to be a valid number literal. `null` converts to zero, `undefined`
converts to `NaN`; the distinction between the two absence values continues here too.

The to-string conversion procedure is also defined per type:

```js
console.log(String(21.5));
console.log(String(null), String(undefined), String(true));
console.log(String([1, 2, 3]));
console.log(String([]));
console.log(String([null, undefined, 3]));
console.log(String({ a: 1 }));
```

```
21.5
null undefined true
1,2,3

,,3
[object Object]
```

Arrays join their elements with commas; an empty array gives an empty string. `null` and
`undefined` values inside an array convert to an empty string — this is why the fourth
line has two leading commas. Ordinary objects do not show their contents; they give the
fixed text `[object Object]`.

## From Object to Primitive Value

The array and object results above go through an intermediate step. When an object
enters a place expecting a number or string, it is first **converted to a primitive
value**. This conversion tries two of the object's methods in a specific order: in a
numeric context the value-producing method is called first, in a text context the
text-producing method is called first.

The behavior can be observed directly:

```js
const obj = {
  valueOf() { return 42; },
  toString() { return "forty two"; },
};
console.log(obj * 2);
console.log(String(obj));
console.log(obj + 1);
```

```
84
forty two
43
```

Multiplication is a numeric context: the value-producing method was called. Converting to
string is a text context: the text-producing method was called. The third line is the
addition operator's special case; it is the subject of the next section.

In an ordinary object, the value-producing method returns the object itself — that is, it
does not give a primitive value. So the turn passes to the text-producing method, and
`[object Object]` comes out. Arrays give `1,2,3` because they define the text-producing
method to join their elements.

## The Two Faces of the Addition Operator

The addition operator is set apart from the other arithmetic operators. Its steps are
these: both operands are converted to a primitive value; **if either one is a string**,
the result is text concatenation, otherwise both are converted to number and added.

```js
console.log(1 + 2);
console.log("1" + 2);
console.log(1 + 2 + "3");
console.log("1" + 2 + 3);
```

```
3
12
33
123
```

The last two lines show that the operator associates left to right. In the third line,
`1 + 2` is computed numerically first, then the result `3` is converted to string and
concatenated. In the fourth line, the first operation is already concatenation; because
the result is a string, the second operation becomes concatenation too. The order of
operations determines the type of the result.

The other arithmetic operators do not have this duality; they always convert to number:

```js
console.log("21.5" - 1);
console.log("21.5" * 2);
console.log(1 + null);
console.log(1 + undefined);
console.log(true + true);
```

```
20.5
43
1
NaN
2
```

The third and fourth lines are a direct consequence of the to-number conversion rules:
`null` gives zero, `undefined` gives `NaN`, and any arithmetic operation involving `NaN`
produces `NaN`.

The commonly quoted expressions also come out of the same two steps:

```js
console.log([] + {});
console.log([] + []);
```

```
[object Object]

```

First line: an empty array converts to an empty string when converted to primitive, the
object converts to `[object Object]`; because one is a string, concatenation happens.
Second line: two empty strings are concatenated and an empty line is printed. There is no
mystery in the middle — there is only a two-step procedure.

## Explicit Conversion Tools

Writing the conversion instead of relying on coercion makes code easier to read. Four
tools need to be distinguished:

```js
console.log(parseInt("21.5 C"), parseFloat("21.5 C"));
console.log(parseInt("  42px"), Number("  42px"));
console.log(parseInt(""), Number(""));
console.log(parseInt("1f", 16));
console.log(+"21.5", +"", +"abc");
```

```
21 21.5
42 NaN
NaN 0
31
21.5 0 NaN
```

`Number` requires the entire text to be valid; if it is not, it gives `NaN`, and it
counts an empty string as zero. `parseInt` and `parseFloat`, on the other hand, **start
from the beginning and read as far as they can**; they ignore the rest, and give `NaN`
if they cannot read anything at all. `parseInt` also takes a base parameter; the
notations from the Hexadecimal and Octal Bases lesson in the How Computers Work course
are read with this parameter.

The unary plus operator applies the same rule as `Number`; because its brevity comes at
the expense of readability, it will not be used in this course.

The choice between the two tools depends on the data. If a field "must contain only a
number," `Number` is the right choice: it reports corrupted data with `NaN`. If a field
is "text starting with a number," `parseFloat` is the right choice. The wrong choice lets
corrupted data pass through silently.

## Application to the Measurement Script

Measurement records mostly arrive as text. The script below reads semicolon-separated
lines and converts the numeric fields:

```js
const lines = ["A1;21.5;48", "A2;19.75;55", "B1;;41", "B2;abc;60"];

for (const line of lines) {
  const [station, temperatureText, humidityText] = line.split(";");
  const temperature = Number(temperatureText);
  const humidity = Number(humidityText);
  console.log(station, temperature, humidity, Number.isNaN(temperature));
}
```

```
A1 21.5 48 false
A2 19.75 55 false
B1 0 41 false
B2 NaN 60 true
```

The last two lines show two separate kinds of data corruption, and only one of them is
caught.

In the `B2` record, the temperature field says `abc`; `Number` turned this into `NaN`,
and the `Number.isNaN` check reported the state. In the `B1` record, though, the
temperature field is **empty**, and `Number` converted the empty string to zero. Zero is
a valid temperature value; the check assumed this record was sound. A missing measurement
silently turned into zero degrees.

The correct behavior is to test whether the field is empty before conversion:

```js
function readTemperature(text) {
  if (text.trim() === "") {
    return null;
  }
  const value = Number(text);
  return Number.isNaN(value) ? null : value;
}

for (const field of ["21.5", "", "   ", "abc", "0"]) {
  console.log(JSON.stringify(field), "->", readTemperature(field));
}
```

```
"21.5" -> 21.5
"" -> null
"   " -> null
"abc" -> null
"0" -> 0
```

The empty field and the corrupted field are now caught separately, and a valid zero
measurement is preserved. The last line matters for this reason: zero is not missing
data.

An attempt to write the same job more briefly breaks at exactly this point:

```js
function readTemperatureShort(text) {
  const value = Number(text);
  return value ? value : null;
}

for (const field of ["21.5", "", "abc", "0"]) {
  console.log(JSON.stringify(field), "->", readTemperatureShort(field));
}
```

```
"21.5" -> 21.5
"" -> null
"abc" -> null
"0" -> null
```

Three lines are correct, the last one is wrong: a valid zero measurement turned into
missing data. The cause is not the conversion, it is how the condition evaluates the
value `0`. The rule for this behavior will be defined in the Truthy and Falsy Values
lesson.

Choosing `null` for a missing value is deliberate: per the distinction in the Primitive
Types lesson, `null` carries the information "not measured"; `undefined` would say the
field does not exist at all.

## Summary

- Coercion is toward three targets: number, string, and boolean. There is a
  step-by-step-defined procedure in the standard for each target.
- In converting to number, an empty string gives zero, invalid text gives `NaN`, `null`
  gives zero, `undefined` gives `NaN`.
- Objects are first converted to a primitive value; the value-producing method takes
  priority in a numeric context, the text-producing method in a text context.
- The addition operator performs concatenation if, after one of the operands is
  converted to a primitive value, it is a string; the other arithmetic operators always
  convert to number.
- `Number` requires the entire text, `parseInt`/`parseFloat` read as far as they can;
  they are chosen based on the field's meaning.
- Converting an empty string to zero turns a missing measurement into a valid value;
  a blank check is required before conversion.

## Next Step

This lesson used comparisons like `temperature === null` without defining their rules.
The next lesson takes up the three comparison operators step by step: strict equality,
which compares type; loose equality, which applies coercion; and same-value equality,
which separates the `NaN` and `-0` cases. Loose equality's steps are a direct continuation
of this lesson's coercion procedures.
