Explicit type conversion
Explicit type conversion
In many cases JS automatically convert values into appropriate data types if necessary. Sometimes however, you will run into a case where you need to explicitly convert a value to an appropriate type. In figure 1 two numbers, given by the user through a HTML form, need to be added. The output value however, is a concatenation of the two numbers as strings instead of an addition of numbers. To make this little application work properly, the input values need to be explicitly converted to numbers first. The example in figure 1 contains JavaScript you may not be familiar with yet. This example just serves to point out that explicit conversion to numbers is often needed when performing mathematical operations on values from a string-based source, such as HTML form elements.
<form>
<input name="firstNumber" type="number" value="1" />
<span>+</span>
<input name="secondNumber" type="number" value="1" />
<span>=</span>
<output name="outputNumber" for="firstNumber secondNumber"></output>
</form>
<script>
'use strict';
const formElements = document.querySelector("form").elements;
const firstNumber = formElements["firstNumber"];
const secondNumber = formElements["secondNumber"];
const outputNumber = formElements["outputNumber"];
function outputFunction() { outputNumber.value = firstNumber.value + secondNumber.value; }
outputFunction();
firstNumber.addEventListener('input', outputFunction);
secondNumber.addEventListener('input', outputFunction);
</script>
Result:
firstNumber.value
and secondNumber.value
need to be explicitly converted to numbers before adding.
The shortest way to convert a non-number to a number is to use the unary plus operator: +true === 1
.
Another way is to multiply by 1: (1 * "") === 0
(or divide by 1 or subtract zero or...). In fact, both of these operations use JavaScript's implicit type conversion,
or type coercion, as described previously. So, explicit conversion using implicit conversion 🙄.
Also bitwise operators coerce to numbers, so
you can also use methods like ~~x
or x>>0
to explicitly convert x
to a number.
To convert a non-boolean value to a boolean value you can use a double NOT operator (!!
).
console.log(+" 10 "); // logs: 10
console.log(1 * null); // logs: 0
console.log(!!1); // logs: true
The general way to explicitly convert primitive values is to use the wrapper object constructor functions.
But do not use instances of wrapper objects to convert values.
Converting a value to a number is done by using Number(value)
, converting to a boolean is done by Boolean(value)
etc.
let myNum = new Number("2"); // do not use this!
console.log(Number("2")); // logs: 2
console.log(Number(" 10 ")); // logs: 10
console.log(Number("1 0")); // logs: NaN
console.log(Number("hello")); // logs: NaN
console.log(Number("10g")); // logs: NaN
console.log(Number("")); // logs: 0
console.log(Number("123e-2")) // logs: 1.23
console.log(Number("-Infinity")) // logs: -Infinity // typeof -Infinity is number!
console.log(Number("null")); // logs: NaN
console.log(Number(null)); // logs: 0
console.log(Number(undefined)); // logs: NaN
console.log(Number(true)); // logs: 1
console.log(Number(false)); // logs: 0
console.log(Number('' + null)); // logs: NaN // ('' + null) returns "null" as a string which is then converted to a number, resulting in NaN.
console.log(Number('2' + 2)); // logs: 22 // String 22 converts to number 22.
console.log(Number("" + "")); // logs: 0 // ("" + "") returns an empty string which then converts to 0.
// The falsy values:
console.log(Boolean(0)); // logs: false
console.log(Boolean(0n)); // logs: false
console.log(Boolean("")); // logs: false
console.log(Boolean(null)); // logs: false
console.log(Boolean(undefined)); // logs: false
console.log(Boolean(NaN)); // logs: false
console.log(Boolean(1)); // logs: true
console.log(Boolean("0")); // logs: true
console.log(Boolean(1e100)); // logs: true
console.log(Boolean('hello')); // logs: true
console.log(String(true)); // logs: "true" // a string.
console.log(String(null)); // logs: "null" // a string.
console.log(BigInt(55)); // logs: 55n
Converting to strings
A (less reliable) alternative to the String()
constructor function is the toString()
method that both
the number and boolean wrapper objects provide
(see chapter Object to primitive conversion).
Method toString()
takes an optional parameter to indicate a radix or base of a numeral system.
This way toString()
can also be used to convert base 10 numbers to numeral representations in an another base, returned as a string.
console.log(15.toString()); // logs: SyntaxError // 15 is conceived as an identifier, and identifiers cannot start with a digit.
let varToStr = 3.14;
console.log(varToStr.toString()); // logs: "3.14" // as a string.
varToStr = true;
console.log(varToStr.toString()); // logs: "true" // as a string.
varToStr = null;
console.log(varToStr.toString()); // logs: TypeError // null has no wrapper object constructor and thus no toString() method.
varToStr = undefined;
console.log(varToStr.toString()); // logs: TypeError // undefined also has no properties or methods.
varToStr = 10;
console.log(varToStr.toString(2)); // logs: "1010" // converted to a binary (base 2) number, returned as a string.
Converting to numbers
Explicit number conversion using Number()
or unary +
also converts non-negative binary, octal and hexadecimal integers into decimal integers.
In addition, it converts numbers containing underscores (numeric separators) and
numbers represented in the scientific e-notation
into the plain decimal number format (if possible).
console.log(+0xFACE); // logs: 64206
console.log(Number(0xFACE)); // logs: 64206
console.log(+0b11111111) // logs: 255
console.log(Number(0b11111111)); // logs: 255
console.log(+0o144) // logs: 100
console.log(Number(0o144)) // logs: 100
console.log(+1_234.56) // logs: 1234.56
console.log(Number(1_234.56)) // logs: 1234.56
console.log(+123e-2) // logs: 1.23
console.log(Number(123e-2)) // logs: 1.23
console.log(+123e19) // logs: 1.23e+21
console.log(Number(123e19)) // logs: 1.23e+21
Explicit number conversion returns 0
when converting an empty string (""
, including a string with only spaces).
In some cases it may not be desirable when an empty string converts to number zero, for instance when a user left an input field open.
The same thing holds for converting value null
(Number(null) === 0
).
To "fix" this you can use something like:
function toNumber(value) {
if (typeof value === 'number') { return value; } // if value is a number, no conversion
else if (value === null) { return NaN }
else if (typeof value === 'string' && value.trim().length === 0) { return NaN; } // check for empty string. trim() removes whitespace from both sides of a string.
else { return +value; } // in all other cases return the value converted to a number
}
console.log(toNumber("2e10")); // logs: 20000000000
console.log(toNumber(" ")); // logs: NaN
console.log(toNumber("123p")); // logs: NaN
console.log(toNumber(null)); // logs: NaN
console.log(toNumber(true)); // logs: 1
The Number
object also provides separate methods to convert a string to a number.
The method Number.parseFloat()
converts its argument to a number
(which in JS is a floating point number),
only a little bit different than when using the Number()
constructor function.
Number.parseFloat()
does return NaN
to an empty string and to null
.
It also converts boolean values (true
, false
) to NaN
. It converts to a number if
the first non-whitespace character can be converted to a number, even if following characters are non-digits.
In some applications this may be undesirable.
console.log(Number.parseFloat("2e10")); // logs: 20000000000
console.log(Number.parseFloat(" ")); // logs: NaN
console.log(Number.parseFloat("123p")); // logs: 123 // in many applications you would want this to return NaN.
console.log(Number.parseFloat(null)); // logs: NaN
console.log(Number.parseFloat(true)); // logs: NaN
The method Number.parseInt()
parses a string argument to an integer (a whole number floating point).
It converts the string to a number up until it encounters a character that is not a part of a number.
Next it truncates the number to an integer value (it removes any fractional digits).
Preceding and succeeding white spaces in a string are ignored.
It returns NaN
if the (first) argument cannot be converted to a number at all, this includes the empty string ""
.
It also returns NaN
if the (first) argument is not a string.
An optional second argument is allowed to indicate the radix or base of the numeral system the number should be converted in.
console.log(Number.parseInt(" 3.99 ")); // logs: 3
console.log(Number.parseInt("3.01")); // logs: 3
console.log(Number.parseInt(" -15.99 ")); // logs: -15
console.log(Number.parseInt("23e100")); // logs: 23 // while 23e100 IS an integer!
console.log(Number.parseInt("23*2")); // logs: 23
console.log(Number.parseInt("-23px")); // logs: -23
console.log(Number.parseInt("$25")); // logs: NaN
console.log(Number.parseInt("Infinity")); // logs: NaN
console.log(Number.parseInt("")); // logs: NaN
console.log(Number.parseInt(NaN)); // logs: NaN
console.log(Number.parseInt(null)); // logs: NaN
console.log(Number.parseInt(undefined)); // logs: NaN
console.log(Number.parseInt(true)); // logs: NaN
console.log(Number.parseInt(false)); // logs: NaN
console.log(Number.parseInt(101, 2)); // logs: 5
console.log(Number.parseInt(102, 3)); // logs: 11
Like Number.parseFloat()
, Number.parseInt()
converts the string to a number up until it encounters a character that is not a part of a number.
In some situations this may be undesirable, in other situations this may be very useful, for example when parsing CSS unit values:
<div id="myContainer" style="width:200px;"></div>
const containerWidth = document.querySelector('#myContainer').style.width;
console.log(containerWidth); // logs: 200px
console.log(parseInt(containerWidth, 10)); // logs: 200
console.log(Number(containerWidth)); // logs: NaN
Number.parseInt()
has a nasty quirk.
Truncating large or small numbers (actual numbers instead of strings) may produce unexpected results.
console.log(Number.parseInt("0.0000004")); // logs: 0
console.log(Number.parseInt(0.0000004)); // logs: 4 // incorrect truncation
console.log(Number.parseInt("1000000000000000000000")); // logs: 1e+21
console.log(Number.parseInt(1000000000000000000000)); // logs: 1 // incorrect truncation
The Math
object provides better options for truncating or rounding floating point numbers to integers.
They coerce parameters into a number.
Method
Math.trunc()
truncates a number to an integer.
Methods
Math.floor()
and
Math.ceil()
also round-off to an integer, but in different ways than Math.trunc()
.
function toInt(value) {
if (typeof value === 'string' && value.trim().length === 0) { return NaN; } // check for empty string. trim() removes whitespace from both sides of a string.
else { return Math.trunc(value); } // in all other cases return the value converted to a number and truncate.
}
console.log(toInt("15e2")); // logs: 1500
console.log(toInt("15px")); // logs: NaN //!
console.log(toInt("")); // logs: NaN // !
console.log(toInt(" ")); // logs: NaN // !
console.log(toInt("Infinity")); // logs: Infinity // !
console.log(toInt("1000000000000000000000")); // logs: 1e+21
console.log(toInt(1000000000000000000000)); // logs: 1e+21
console.log(toInt(null)); // logs: 0 // !
console.log(toInt(undefined)); // logs: NaN
console.log(toInt(true)); // logs: 1 // !
console.log(toInt(false)); // logs: 0 // !
console.log(toInt(" -3.99 ")); // logs: -3
console.log(toInt(3.99)); // logs: 3
console.log(toInt("-3.99")); // logs: -3
console.log(Math.floor("-3.99")); // logs: -4
console.log(Math.ceil("-3.99")); // logs: -3
console.log(toInt("3.99")); // logs: 3
console.log(Math.floor("3.99")); // logs: 3
console.log(Math.ceil("3.99")); // logs: 4
Validating numbers
In many cases you need to check that a value is a valid number. You can easily do this by using if (typeof value === 'number') { }
.
However, also value NaN
(is of data type number) would validate in this case. We can 'fix' this by using a function like:
// function returns true or false:
function isValidNumber(value) {
return typeof value === 'number' && !Number.isNaN(value);
}
console.log(isValidNumber("NaN")); // logs: false
console.log(isValidNumber(NaN)); // logs: false
console.log(isValidNumber(0/0)); // logs: false
console.log(isValidNumber(null)); // logs: false
console.log(isValidNumber(true)); // logs: false
console.log(isValidNumber(2.5e6)); // logs: true
console.log(isValidNumber(undefined)); // logs: false
console.log(isValidNumber(-Infinity)); // logs: true
Additionally we likely want to check if the number is finite as well.
The Number.isFinite()
method
returns false
if the argument is not a number,
if it is (positive or negative) Infinity
,
or if it is NaN
.
In all other cases it returns true
. We can replace the return value of the function in the above example by Number.isFinite()
:
function isValidNumber(value) {
return Number.isFinite(value);
}
console.log(isValidNumber("NaN")); // logs: false
console.log(isValidNumber(NaN)); // logs: false
console.log(isValidNumber(0/0)); // logs: false
console.log(isValidNumber(null)); // logs: false
console.log(isValidNumber(true)); // logs: false
console.log(isValidNumber(2.5e6)); // logs: true
console.log(isValidNumber(undefined)); // logs: false
console.log(isValidNumber(-Infinity)); // logs: false
BTW: Of course it is also possible to directly use Number.isFinite(value);
. However, wrapping it in a function isValidNumber
is clearer and more sustainable. It makes it clear that you are validating some input and when in the future validation requires some additional check,
you can simply add it to the function isValidNumber
, instead of replacing Number.isFinite(value)
everywhere it is used.
In other instances we may need to check if a value (e.g. user input) is an integer.
To check if a number is an integer we can use
Number.isInteger()
.
Number.isInteger()
always returns false
if its argument is not a number, including NaN
and Infinity
.
console.log(Number.isInteger(2099999999999999)); // logs: true
console.log(Number.isInteger(1/2)); // logs: false
console.log(Number.isInteger("15")); // logs: false
console.log(Number.isInteger(" ")); // logs: false
console.log(Number.isInteger(15e2)); // logs: true
console.log(Number.isInteger(-0)); // logs: true
console.log(Number.isInteger(NaN)); // logs: false
console.log(Number.isInteger(true)); // logs: false
console.log(Number.isInteger(null)); // logs: false
console.log(Number.isInteger(undefined)); // logs: false
console.log(Number.isInteger(-Infinity)); // logs: false
Summarised
Number conversion & validation
function toNumber(value) {
// conversion only needed if data type is not a number:
if (!(typeof value === 'number')) {
// optional exceptions to standard conversion:
if (value === null) { value = NaN } // check for null.
else if (typeof value === 'string' && value.trim().length === 0) { value = NaN; } // check for empty string. trim() removes whitespace from both sides of a string.
//
else { value = +value; } // standard conversion in all other cases
}
if (Number.isFinite(value)) { return value; } // if valid number, return value
else { throw new TypeError("Input cannot be converted into a number."); }
}
function printNumber(num) {
num = toNumber(num);
console.log(num);
}
printNumber(13); // logs: 13
printNumber("2.5e6"); // logs: 2500000
printNumber(0/0); // TypeError: Input cannot be converted into a number.
printNumber(true); // logs: 1 // It does convert booleans!
printNumber(null); // TypeError: Input cannot be converted into a number.
printNumber(undefined); // TypeError: Input cannot be converted into a number.
printNumber(" "); // TypeError: Input cannot be converted into a number.
printNumber(Infinity); // TypeError: Input cannot be converted into a number.
function isValidNumber(value) {
return Number.isFinite(value);
}
Integer conversion
function toInt(value) {
// optional exceptions to standard conversion:
if (typeof value === 'string' && value.trim().length === 0) { value = NaN; } // check for empty string. trim() removes whitespace from both sides of a string.
if (typeof value === 'boolean') { value = NaN; } // check for boolean values true and false
else if (value === null) { value = NaN } // check for null.
//
else { value = Math.trunc(value); } // in all other cases conversion using trunc().
if (Number.isInteger(value)) { return value }
else { throw new TypeError("Input cannot be converted into an integer."); }
}
function printNumber(num) {
num = toInt(num);
console.log(num);
}
printNumber(2099999999999999); // logs: 2099999999999999
printNumber(1/2); // logs: 0
printNumber("15"); // logs: 15
printNumber(15e2); // logs: 1500
printNumber(-0); // logs: -0
printNumber(" "); // TypeError: Input cannot be converted into an integer.
printNumber(NaN); // TypeError: Input cannot be converted into an integer.
printNumber(true); // TypeError: Input cannot be converted into an integer.
printNumber(null); // TypeError: Input cannot be converted into an integer.
printNumber(undefined); // TypeError: Input cannot be converted into an integer.
printNumber(-Infinity); // TypeError: Input cannot be converted into an integer.