eslint-plugin-big-number-rules
v2.2.0
Published
Enforce finance-safe calculations using BigNumber instead of native JavaScript arithmetic and Math functions.
Downloads
1,820
Maintainers
Readme
✊ Enforce 💰 finance-safe 🧷 calculations using bignumber.js (or something similar!) instead of native JavaScript arithmetic and Math functions.
$ pnpm i eslint-plugin-big-number-rules --save-dev
Configuration
After installation, make the plugin available to your eslint
:
// .eslintrc
{
"plugins": ["big-number-rules"]
}
Recommended rules will warn
about everything:
// .eslintrc
{
"plugins": ["big-number-rules"],
"extends": ["plugin:big-number-rules/recommended"]
}
"Everything" means this:
// .eslintrc
{
"plugins": ["big-number-rules"],
"rules": {
"big-number-rules/arithmetic": "warn",
"big-number-rules/assignment": "warn",
"big-number-rules/bitwise": "warn",
"big-number-rules/comparison": "warn",
"big-number-rules/isNaN": "warn",
"big-number-rules/math": "warn",
"big-number-rules/number": "warn",
"big-number-rules/parseFloat": "warn",
"big-number-rules/rounding": "warn"
},
"settings": {
"big-number-rules": {
// Specify the following if you want rules to
// apply only to files with this declaration:
//
// import ... from 'bignumber.js'
//
"importDeclaration": "bignumber.js",
// Optionally, you can also apply rules only when
// importing the desired specifier from such
// declarations:
//
// import BigNumber from 'bignumber.js'
//
"importSpecifier": "BigNumber",
// Optionally, you can disable suggestions for
// one or more infix operators using the following
// setting:
//
"unsafelyIgnoreSuggestionsForOperators": []
//
// Example:
//
// "unsafelyIgnoreSuggestionsForOperators": ["+", "+="]
}
}
}
You can also customise the transformations.
Example transforms:
| from | to | plugin will also suggest |
| -------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------------------------------- |
| 0.1 + 0.2
| BigNumber.sum(0.1, 0.2)
| ('').concat(0.1, 0.2)
and `${0.1}${0.2}`
when you want string-concatenation instead of financial arithmetic |
| result === 0.3
| BigNumber(result).isEqualTo(0.3)
| Object.is(result, 0.3)
when you want to strictly compare things that are not financial calculations |
| 19.99 * 0.1
| BigNumber(19.99).multipliedBy(0.1)
|
| 1 < 2
| BigNumber(1).isLessThan(2)
|
| 2 >>> 4
| BigNumber(2).shiftedBy(4)
|
| 4 << 2
| BigNumber(4).shiftedBy(-2)
|
| Math.min(1, 2)
| BigNumber.minimum(1, 2)
|
| Math.sign(-6)
| BigNumber(-6).comparedTo(0)
|
| (1).toFixed(2)
| BigNumber(1).toFixed(2)
|
| parseFloat('1.2')
| BigNumber('1.2')
|
| Number.parseFloat('2.1')
| BigNumber('2.1')
|
Can keep a chain going...
BigNumber.sum(0.1, 0.2) - 0.3
// --> BigNumber.sum(0.1, 0.2).minus(0.3)
3 ** BigNumber(1).plus(2)
// --> BigNumber(3).exponentiatedBy(BigNumber(1).plus(2))
But why?
If you use floating-points for currency (instead of whole-numbers like you probably should) libraries like bignumber.js help keep your code away from the binary floating-point pitfalls of IEEE-754:
const sum = 0.1 + 0.2
sum === 0.3
// false
sum
// 0.30000000000000004
This is the classic example and is often cited, but there are other rare corner-cases that will eventually be caught some time after committing to a currency-unsafe solution.
eslint-plugin-big-number-rules
will translate the example above to:
const sum = BigNumber.sum(0.1, 0.2)
BigNumber(sum).isEqualTo(0.3)
// true
The problem manifests in the first place because in the floating-point number-type of most languages (not just JavaScript!) the mantissa/significand is represented as a power-of-two fraction rather than a power-of-10 decimal:
_ _._____._____._____._____._____._____._____.______.______.__ _ _
_ _| 8 | 4 | 2 | 1 | 1/2 | 1/4 | 1/8 | 1/16 | 1/32 | ... etc
\__________.___________/ \______________________________ _ _ _
Exponent ------^ |
|
Significand ------>-------->----------^
IEEE-754 defines various rules for marshalling these fractions into a decimal, but as you can probably imagine it's not always exact.
Libraries like bignumber.js
helps us work around this. Using them isn't complicated, but it does require a little discipline and vigilance to keep on top of, so an eslint plugin to warn-about the use of JavaScript's native-math methods seemed like a good way to do that.
But I use +
for string-concatenation!
Since v2.0.0
the plugin will now offer String#concat()
and Template String
replacements for +
related rules, in addition to the default BigNumber suggestion:
0.1 + 0.2
// -> BigNumber.sum(0.1, 0.2)
// -> ('').concat(0.1, 0.2)
// -> `${0.1}${0.2}`
result += 0.3
// -> result = BigNumber(result).plus(0.3)
// -> result = ('').concat(result, 0.3)
// -> result = `${result}${0.3}`
It will also offer Object.is()
as a suggestion for ===
related rules:
0.1 === 0.2
// -> BigNumber(0.1).isEqualTo(0.2)
// -> Object.is(0.1, 0.2)
0.1 !== 0.2
// -> !BigNumber(0.1).isEqualTo(0.2)
// -> !Object.is(0.1, 0.2)
I want to take the risk of ignoring certain infix operators!
Since v2.1.0
you can use the unsafelyIgnoreSuggestionsForOperators
option to ignore one or more suggestions for the infix operators:
// .eslintrc
{
"plugins": ["big-number-rules"],
"settings": {
"big-number-rules": {
"unsafelyIgnoreSuggestionsForOperators": ["+", "+="]
}
}
}
Credits
eslint-plugin-big-number-rules
was written by Conan Theobald.
He was inspired by the work of these fine Internet folk:
🙏
Contributing
If you'd like to offer a material contribution, I like coffee ☕️ :)
License
MIT licensed: See LICENSE