2.1 What is type coercion?
Each operation (function, operator, etc.) expects its parameters to have certain types. If a value doesn’t have the right type for a parameter, three common options for, e.g., a function are:
The function can throw an exception:
function multiply(x, y) {
if (typeof x !== 'number' || typeof y !== 'number') {
throw new TypeError();
}
// ···
}
The function can return an error value:
function multiply(x, y) {
if (typeof x !== 'number' || typeof y !== 'number') {
return NaN;
}
// ···
}
The function can convert its arguments to useful values:
function multiply(x, y) {
if (typeof x !== 'number') {
x = Number(x);
}
if (typeof y !== 'number') {
y = Number(y);
}
// ···
}
In (3), the operation performs an implicit type conversion. That is called type coercion.
JavaScript initially didn’t have exceptions, which is why it uses coercion and error values for most of its operations:
// Coercion
assert.equal(3 * true, 3);
// Error values
assert.equal(1 / 0, Infinity);
assert.equal(Number('xyz'), NaN);
However, there are also cases (especially when it comes to newer features) where it throws exceptions if an argument doesn’t have the right type:
Accessing properties of
null
orundefined
:> undefined.prop
TypeError: Cannot read property 'prop' of undefined
> null.prop
TypeError: Cannot read property 'prop' of null
> 'prop' in null
TypeError: Cannot use 'in' operator to search for 'prop' in null
Using symbols:
> 6 / Symbol()
TypeError: Cannot convert a Symbol value to a number
Mixing bigints and numbers:
> 6 / 3n
TypeError: Cannot mix BigInt and other types
New-calling or function-calling values that don’t support that operation:
> 123()
TypeError: 123 is not a function
> (class {})()
TypeError: Class constructor cannot be invoked without 'new'
> new 123
TypeError: 123 is not a constructor
> new (() => {})
TypeError: (intermediate value) is not a constructor
Changing read-only properties (only throws in strict mode):
> 'abc'.length = 1
TypeError: Cannot assign to read only property 'length'
> Object.freeze({prop:3}).prop = 1
TypeError: Cannot assign to read only property 'prop'
2.1.1 Dealing with type coercion
Two common ways of dealing with coercion are:
A caller can explicitly convert values so that they have the right types. For example, in the following interaction, we want to multiply two numbers encoded as strings:
let x = '3';
let y = '2';
assert.equal(Number(x) * Number(y), 6);
A caller can let the operation make the conversion for them:
let x = '3';
let y = '2';
assert.equal(x * y, 6);
I usually prefer the former, because it clarifies my intention: I expect x
and y
not to be numbers, but want to multiply two numbers.