2.2.0 โ€ข Published 1 year ago

eslint-plugin-big-number-rules v2.2.0

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

โœŠ 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:

fromtoplugin will also suggest
0.1 + 0.2BigNumber.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.3BigNumber(result).isEqualTo(0.3)Object.is(result, 0.3) when you want to strictly compare things that are not financial calculations
19.99 * 0.1BigNumber(19.99).multipliedBy(0.1)
1 < 2BigNumber(1).isLessThan(2)
2 >>> 4BigNumber(2).shiftedBy(4)
4 << 2BigNumber(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

2.2.0

1 year ago

2.0.0

1 year ago

2.1.0

1 year ago

1.10.2

1 year ago

1.10.1

1 year ago

1.10.0

1 year ago

1.9.1

2 years ago

1.9.0

2 years ago

1.8.2

2 years ago

1.8.1

2 years ago

1.8.0

2 years ago

1.8.5

2 years ago

1.8.4

2 years ago

1.8.3

2 years ago

1.7.8

2 years ago

1.7.7

2 years ago

1.7.6

2 years ago

1.7.5

2 years ago

1.7.4

3 years ago

1.7.3

3 years ago

1.7.2

3 years ago

1.7.1

3 years ago

1.6.2

3 years ago

1.7.0

3 years ago

1.6.1

3 years ago

1.6.0

3 years ago

1.5.6

3 years ago

1.5.5

3 years ago

1.5.4

3 years ago

1.5.3

3 years ago

1.5.2

3 years ago

1.5.1

3 years ago

1.5.0

3 years ago

1.4.0

3 years ago

1.3.4

3 years ago

1.3.3

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.3.0

3 years ago

1.2.2

3 years ago

1.2.1

3 years ago

1.2.0

3 years ago

1.1.0

3 years ago

1.0.6

3 years ago

1.0.5

3 years ago

1.0.4

3 years ago

1.0.3

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.0

3 years ago