# vkarpov15/thecodebarbarian.com

Fetching contributors…
Cannot retrieve contributors at this time
158 lines (117 sloc) 7.8 KB

Converting a string to a number in JavaScript is surprisingly subtle. With `NaN`, implicit radixes, and numbers vs Numbers, there are a lot of ways to shoot yourself in the foot. In this article, I'll cover the tradeoffs of `parseFloat()` vs `Number()` and `Number.isNaN()` vs `isNaN()`. I'll also describe how to enforce these rules with eslint.

The TLDR is you should use [`Number(x)`] to convert generic JavaScript values to numbers if you want to be permissive, or `parseFloat(x)` if you want to be more strict. You should always use `Number.isNaN()` to check if the conversion failed. You should not use the global `isNaN()` function.

```typeof parseFloat('42'); // 'number'
Number.isNaN(Number('42')); // false

typeof parseFloat('fail'); // 'number'
Number.isNaN(Number('fail')); // true```

Using `Number(x)` has several edge cases that may be correct depending on your perspective. You can also use a tool like archetype that handles some of these edge cases for you:

```archetype.to('42', 'number'); // 42

Number(''); // 0
archetype.to('', 'number'); // throws```

Many developers use `+x` to convert a string to a number. The JavaScript language spec states that `+x` is equivalent to `Number(x)`.

```+'42 fail'; // NaN
+({ valueOf: () => '42' }); // 42
+({ toString: () => '42' }); // 42
+(null); // 0
+('  '); // 0```

## What's Wrong With `Number(x)`?

`Number(x)` and `parseFloat(x)` handle edge cases very differently. `parseFloat()` is more permissive when it comes to accepting different strings:

```Number('42 fail'); // NaN
parseFloat('42 fail'); // 42
parseInt('42 fail'); // 42

Number(' 10'); // 10
parseFloat(' 10'); // 10
parseInt(' 10'); // 10```

You might mistakenly assume this means `Number(x)` is safer and more strict. Unfortunately, `Number(x)` is more lax when it comes to whitespace, `null`, and other edge cases. It converts a lot of surprising values to 0. For example:

```Number(null); // 0
Number(''); // 0
Number('    '); // 0
Number(false); // 0
Number({ toString: () => '' }); // 0
Number({ valueOf: () => '  ' }); // 0```

This is because the JavaScript language spec has a fairly complex set of rules for converting values to numbers.

The rules for how `parseFloat()` converts values are simpler. The interpretter must convert the value to a string, trim whitespace, and then check for the longest prefix that matches JavaScript's regular expression definition of a numeric literal.

```// `parseInt()` behaves like `parseFloat()` on these values
parseFloat(null); // NaN
parseFloat(''); // NaN
parseFloat('    '); // NaN
parseFloat(false); // NaN
parseFloat({ toString: () => '' }); // NaN
parseFloat({ valueOf: () => '  ' }); // NaN```

## `Number.isNaN()` vs `isNaN()`

Another nuance of converting values to numbers is that JavaScript doesn't throw an error if it fails to convert a value `x` to a number. It instead returns a special value `NaN`. To make things more confusing, the `typeof` operator reports that `NaN` is a `'number'`.

```Number('fail'); // NaN
typeof Number('fail'); // number```

The reason why `Number.isNaN()` and `isNaN()` exist is because `==` and `===` do not work as expected with `NaN`.

```Number('fail') == Number('fail'); // false
Number('fail') === Number('fail'); // false
Number('fail') == NaN; // false
NaN === NaN; // false```

`Number.isNaN()` was a new feature in ES6, but unfortunately didn't get much attention. `Number.isNaN()` is more robust and you should use it instead of `isNaN()` unless you explicitly mean to use `isNaN()`.

```// Need to use a function because checking `=== NaN` does **not** work
isNaN(Number('fail')); // true
Number.isNaN(Number('fail')); // true```

Here's a handy analogy for understanding the difference: `Number.isNaN()` is to `isNaN()` as `===` is to `==`. The `isNaN()` function converts the given value to a number before checking it the given number is equal to `NaN`.

```isNaN('fail'); // true
isNaN({}); // true

Number.isNaN('fail'); // false
Number.isNaN({}); // false```

On the other hand, `Number.isNaN(x)` returns `false` if `x` is not of type number. You can polyfill `Number.isNaN()` using the below function:

```Number.isNaN = function(x) {
return typeof x === 'number' && isNaN(x);
};```

Conversely, `isNaN(x)` is equivalent to `Number.isNaN(Number(x))`.

When you're checking whether the result of `Number(x)` or `parseFloat(x)` is equal to `NaN`, you're safe using `isNaN()` because you already tried to convert the value to a number. But in general, you should prefer `Number.isNaN()` over `isNaN()` in the same way that you (hopefully) use `===` unless you really know you mean `==`.

## ESLint Rules

You can configure eslint to force you to use `Number.isNaN()` and your choice of `Number()` or `parseFloat()` using the `no-restricted-globals` rule. There's more info on this GitHub issue. Below is an example of a `.eslintrc.yml` that would disallow using global `isNaN()` and `parseFloat()`

```rules:
no-restricted-globals:
- error
- name: isNaN
- name: parseFloat

Requiring `parseFloat()` instead of `Number()` is trickier, but doable with eslint's generic `no-restricted-syntax` rule.

``````rules:
no-restricted-globals:
- error
- name: isNaN
Converting a value to a number in JavaScript is filled with odd edge cases. If you don't want to think about edge cases, you're best off just using `parseFloat()` and `Number.isNaN()`. If you want to be more flexible, you can use `Number()`. Personally, I just use archetype for this because I don't want to worry about checking for `NaN`.
Can't keep up with what's going on in your `node_modules`? Check out JSReport's Slack integration. JSReport posts to a Slack channel whenever an npm package you're watching, like lodash or webpack, publishes a new release.