JavaScriptの小数リテラルの使用を禁止するESLintの作成方法

JavaScriptの小数リテラルとは

JavaScriptにおける小数リテラルとは、ソースコード中に直接書かれた小数(浮動小数点数)の数値そのものを指します。

以下のようなドットを含む数値が小数リテラルです。

0.1
1.5
3.14
10.0
.5

小数リテラルは問題になりやすい

JavaScriptの数値はすべてIEEE 754の浮動小数点数で表現されます。

そのため、「0.1 + 0.2」が「0.3」ではなく「0.30000000000000004」になるなどの問題が発生します。

この問題は例えば「金額 * 消費税」のような簡単な処理でも問題が発生します。

「100 * 1.1」はJavaScriptでは「110」ではなく「110.00000000000001」になるからです。

消費税を1.1にして計算したサンプル

この問題は以下のように消費税に小数リテラルを使わずに計算するか、bignumber.jsなどの小数を正確に計算するためのライブラリを使用すれば解決できます。

const taxRate = 110 // 消費税
const taxRateDiv = 100 // 消費税の除数

// 中略
const result = price ? (price * taxRate / taxRateDiv) : ''

小数リテラルの使用を禁止するESLint

小数リテラルの使用を禁止するESLintを作成すると、小数リテラルが使用されていることがわかりやすくなります。

やり方は、値がnumber型でNumber.isIntegerか判定し、条件に一致しなければ「小数リテラルの使用は禁止されています。」のようなメッセージを返すESLintのコードを以下のように作って返すだけです。

例外的に小数リテラルを使いたいときのために、特定の変数名の場合は小数リテラルを代入してもエラーにならないようにする処理も追加しておくと良いでしょう。

eslint.config.js
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
import { defineConfig, globalIgnores } from 'eslint/config'

const noFloatLiteralRule = {
  meta: {
    type: 'problem',
    docs: {
      description: '小数リテラルの使用を禁止する。'
    }
  },
  create(context) {
    return {
      Literal(node) {
        if (typeof node.value !== 'number') return
        if (Number.isInteger(node.value)) return

        const parent = node.parent

        // 以下の変数名の小数リテラルの場合は無視する
        if (
          parent?.type === 'VariableDeclarator' &&
          [''].includes(parent.id.name)
        ) {
          return
        }

        context.report({
          node,
          message: '小数リテラルの使用は禁止されています。'
        })
      }
    }
  }
}

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    plugins: {
      custom: {
        rules: {
          'no-float-literal': noFloatLiteralRule
        }
      }
    },
    rules: {
      'custom/no-float-literal': 'error'
    },
    extends: [
      js.configs.recommended,
      tseslint.configs.recommended,
      reactHooks.configs.flat.recommended,
      reactRefresh.configs.vite,
    ],
    languageOptions: {
      ecmaVersion: 2020,
      globals: globals.browser,
    },
  },
])

上記のルールが設定されている場合は、変数に小数リテラルを代入するとエラーになります。

JavaScriptの小数リテラルの使用を禁止するESLintの作成方法

JavaScriptでは小数リテラルが原因でバグが発生してしまうケースが多いので、ESLintにルールを設定しておいて、なるべく使用しないようにすればバグの発生する可能性が低くなります。

特に理由がなければESLintのルールに追加することを推奨します。