Published on 8/1/2021 by
NevuloJavaScript (JS) isn't exactly
for being robust or terse - it's quite the opposite, it stands behind the notion of "I'll take whatever you throw at me". JS is a , developers won't have to explicitly define the type of data they're using. This can make things a lot simpler, but also a lot more complicated at the same time as we don't know what the data we are receiving will be or what we'll be able to do with it.Let's take a simple addNumbers
function:
1function addNumbers(first, second) {2 return first + second;3}
1addNumbers(1, 2); // logs 32typeof addNumbers(1, 2); // logs "number"
Great! We can feed two arguments in as numbers and get the sum as a number for the result. That means we can do number specific operations like .toFixed
. But if we pass in a string as the first argument...
1addNumbers("1", 2); // logs "12"2typeof addNumbers("1", 2); // logs "string"
Now our add function, intended for adding numbers is returning text! Even worse, if we were expecting a number to be returned and we tried using a number-specific method like .toFixed
, our app would throw an error:
addNumbers(...).toFixed is not a function
The situation above is unlikely to happen but not impossible (developer mistake). What is more likely is your app accepting user input, and we need to add validation in our addNumbers
function so that our app isn't just trying to add any two things together. But how can we transform the two arguments in addNumbers
into numbers and then confirm they are valid?
parseInt
parseInt
(also available under Number.parseInt
with same functionality) accepts two parameters, the first being the value to attempt to coerce into a number and the second is the radix or base.
1parseInt("100", 10); // 100
It's good to explicitly set the radix as 10
(since we count in base 10) as if the input string begins with "0x", this represents an octal and the default radix will change to 16 (hexadecimal), producing different results.
An oddity to note with parseInt
is that it will parse the string character by character until it finds an invalid character for the specified radix. To show this better, see below how the same input can result in two different outputs:
1parseInt("0xf", 10); // 0, stops when reaching "x"2parseInt("99blake", 10); // 99, stops when reaching "b"3parseInt("0xf", 16); // 15, 0xf is 15 in hexadecimal (base 16)
isNaN()
isNaN
is a function available in the global scope and it allows us to know if a given input is "not a number" (NaN). We can inverse this to check if an input is a number.
1!isNaN(1); // === true, 1 is a number!
Great, we should expect to see that entering a string would return false
because a string is obviously not a number.
1!isNaN(""); // === true, what??
This function only validates that the value, when coerced to a number using the Number
constructor does not strictly equal itself. A quick reminder on a weird quirk: NaN
is the only value in JS that does not strictly equal itself:
1NaN === NaN; // false
Internally, isNaN
works similarly to this pseudocode:
1function parseInt(value) {2 const n = Number(value);3 return n !== n;4}
So when we convert an empty string to a number using the Number
constructor, what do we get?
1Number(""); // 020 === 0; // true3// therefore..4isNaN(""); // false
If you actually only want to test that a value is/isn't strictly NaN
, prefer Number.isNaN()
instead, which is more robust and also ensures that the type of the value is number
using typeof
typeof x === "number"
The typeof
operator returns the type of the data after it as a string:
1let a = 1;2typeof a; // "number"
This is more accurate than isNaN
as it will not accept strings since typeof (any string)
evaluates to "string"
Number.isFinite
and Number.isInteger
Number.isFinite
only returns true if the value given is of type "number" and the value is within positive infinity and negative infinity (basically any valid number).
If you want to ensure that your data is strictly an integer (not a floating point number like 2.31412
), prefer Number.isInteger
instead which will return false
when provided with a floating point number that cannot be represented as an integer. Note that floating point numbers that are large enough will be rounded and then can be represented as an integer, thus passing the check:
1Number.isInteger(5.0); // true2Number.isInteger(5.000000000000001); // false3Number.isInteger(5.0000000000000001); // true
1function addNumbers(first, second) {2 // parseFloat allows us to convert a string into3 // a number with floating point precision4 first = parseFloat(first);5 second = parseFloat(second);6 // are the arguments valid numbers after being converted from a string?7 if (!Number.isFinite(first))8 throw new Error("first argument must be an integer");9 if (!Number.isFinite(second))10 throw new Error("second argument must be an integer");11 return first + second;12}
1addNumbers("1", 2); // 3! yay2// also works with other wonky combinations3addNumbers("1", "1"); // 24addNumbers("1", "1"); // 25addNumbers(1, "1"); // 26// errors when passing an invalid number7addNumbers("blablah blah", 2);8addNumbers("-", 2); // Error: first argument must be an integer9addNumbers(1); // Error: second argument must be an integer
Subscribe to the Nevuletter so you never miss new posts.
Comments
• 0
You need to be signed in to comment