Lesson 08 / 21
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.
Contents
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:
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:
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:
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.
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:
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:
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:
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:
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:
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:
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,nullgives zero,undefinedgivesNaN. - 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.
Numberrequires the entire text,parseInt/parseFloatread 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.
To keep your progress and take notes, Log in
My notes
Log in to take notes.