eslint-plugin-unicorn
eslint-plugin-unicorn copied to clipboard
Rule proposal: Number(str) over parseFloat(str)
Description
In most cases, Number(string) produces better results than parseFloat(string):
Number.parseFloat("420") // 420
Number.parseFloat("-420") // -420
Number.parseFloat("420.69") // 420.69
Number.parseFloat("420,69") // 420 (NaN would have been better)
Number.parseFloat("420n") // 420 (NaN would have been better)
Number.parseFloat("420_69") // 420 (NaN or 42069 would have been better)
Number.parseFloat("0b1000101") // 0 (NaN or 69 would have been better)
Number.parseFloat("0x45") // 0 (NaN or 69 would have been better)
Number.parseFloat("") // NaN
Number.parseFloat(" \n") // NaN
Number("420") // 420
Number("-420") // -420
Number("420.69") // 420.69
Number("420,69") // NaN
Number("420n") // NaN
Number("420_69") // 42069
Number("0b1000101") // 69
Number("0x45") // 69
Number("") // 0 (NaN would have been better)
Number(" \n") // 0 (NaN would have been better)
If you want to protect against those last two cases, it's a lot easier to do it with Number() than parseFloat():
function safeParseFloat(input: string) {
if (input.trim() === "") {
return Number.NaN
}
return Number(input)
}
Fail
parseFloat(input)
Number.parseFloat(input)
Pass
N/A
Additional Info
Here is a starting place for an eslint rule: https://astexplorer.net/#/gist/1569a1b6990fa03c94c76393b2ccc6a3/5e9b8eae2e48277a76e6f3680d1001b13e1d22c3
I generally also prefer Number to parseInt, as the intent is to transform a stringified number rather than extracting a number from a "maybe-compatible" string. A function like this also covers the rest of the cases:
https://github.com/refined-github/refined-github/blob/4232313404c9d4afaa711217ce539680b7e3799e/source/helpers/loose-parse-int.ts#L10
Correction: Number("420_69") results in NaN today, which I find surprising and isn't noted by the spec as a difference between NumericLiteral and StringNumericLiteral, I'm going to ask TC39 about that.
Those differences are useful to note here though, there are more differences than I thought there were:
Some differences should be noted between the syntax of a StringNumericLiteral and a NumericLiteral:
- A StringNumericLiteral may include leading and/or trailing white space and/or line terminators.
- A StringNumericLiteral that is decimal may have any number of leading 0 digits.
- A StringNumericLiteral that is decimal may include a + or - to indicate its sign.
- A StringNumericLiteral that is empty or contains only white space is converted to +0𝔽.
Infinityand-Infinityare recognized as a StringNumericLiteral but not as a NumericLiteral.- A StringNumericLiteral cannot include a BigIntLiteralSuffix.
I still think this is better than parseFloat(str) though, these differences are still easier to protect against if you want to.
I'd like forbid Number.parseFloat(), Number.parseInt() with (radix 10).
// Bad
Number.parseFloat(foo)
// Good
Number(foo)
// Bad
Number.parseInt(foo, 10)
// Good
Number.parseInt(foo, 8)
Number(foo)
Math.round(Number(foo))
Math.floor(Number(foo))
Math.ceil(Number(foo))
Sounds good to me. The closest version would be with trunc to support negative values:
const foo = '-10.1'
- Number.parseInt(foo, 10)
+ Math.trunc(Number(foo))
Unfortunately this rule can't be auto fixed though
Forgot Math.trunc().
Accepted
I'd like forbid
Number.parseInt()with (radix10).
That would become annoying if you where to use closure compiler adv mode to optimize your code example: playground
I'm not sure what do you mean.

See this warning message, closure compiler complains if you omit radix 10. it wants you to be explicit about it
Nobody is saying anything about omit radix.
@jimmywarting His point is that Number.parseInt(x, 10) should be replaced by Math.floor(Number(x)) or similar - not to omit the radix.