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 .isNaN( Number ( '42' )); typeof parseFloat ( 'fail' ); Number .isNaN( Number ( 'fail' ));

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' ); Number ( '' ); archetype.to( '' , 'number' );

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' ; +({ valueOf: () => '42' }); +({ toString: () => '42' }); +( null ); +( ' ' );

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' ); parseFloat ( '42 fail' ); parseInt ( '42 fail' ); Number ( ' 10' ); parseFloat ( ' 10' ); parseInt ( ' 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 ); Number ( '' ); Number ( ' ' ); Number ( false ); Number ({ toString: () => '' }); Number ({ valueOf: () => ' ' });

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.

parseFloat ( null ); parseFloat ( '' ); parseFloat ( ' ' ); parseFloat ( false ); parseFloat ({ toString: () => '' }); parseFloat ({ valueOf: () => ' ' });

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' ); typeof Number ( 'fail' );

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

Number ( 'fail' ) == Number ( 'fail' ); Number ( 'fail' ) === Number ( 'fail' ); Number ( 'fail' ) == NaN ; NaN === NaN ;

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() .

isNaN ( Number ( 'fail' )); Number .isNaN( Number ( 'fail' ));

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' ); isNaN ({}); Number .isNaN( 'fail' ); Number .isNaN({});

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 message: Use `Number.isNaN()` instead - name: parseFloat message: Use `Number()` instead

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

rules: no-restricted-globals: - error - name: isNaN message: Use `Number.isNaN()` instead no-restricted-syntax: - error - selector: CallExpression[callee.name='Number'] message: Do not use `Number()`, use `parseFloat()` instead

Moving On

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 .