JavaScript Tips | 基本・共通ユーティリティ:安全処理 – 安全な数値計算

JavaScript JavaScript
スポンサーリンク

なぜ「安全な数値計算」が業務で重要になるのか

業務コードの数値計算は、単なる足し算・引き算では終わりません。
外部 API のレスポンス、フォーム入力、DB の値など、「本当に数値か分からないもの」を相手に計算します。

そこでよく起きるのが次のような事故です。

"10" と数値を足しておかしな結果になる
NaN が一度混ざって、以降の計算結果が全部 NaN になる
0 で割って Infinity が出て、そのまま画面に表示される
nullundefined をそのまま計算に使ってしまう

「安全な数値計算」というのは、こうした事故を防ぐために
計算の前後で「値が本当に“まともな数値”かどうか」をチェックしながら計算する、という考え方です。


JavaScript の数値まわりの落とし穴を整理する

number 型なのに「壊れている値」がある

JavaScript の number 型には、次のような特殊値があります。

NaN
Infinity
-Infinity

そして、これらはすべて typeof value === "number" で true になります。
つまり、「number だから安全」とは全く言えません。

一度 NaN が混ざると、以降の計算はほぼ全部 NaN になります。

const a = Number("abc"); // NaN
const b = a + 10;        // NaN
const c = b * 2;         // NaN
JavaScript

Infinity も同様で、0 で割ったりすると簡単に出てきます。

const x = 10 / 0; // Infinity
JavaScript

これらを「安全な数値」と同じ扱いで計算に使うと、業務ロジックが壊れます。


安全な数値判定ユーティリティをまず用意する

有限の数値かどうかを判定する isFiniteNumber

安全な数値計算の土台は、「この値は計算に使っていいか?」を判定する関数です。

function isFiniteNumber(value) {
  return typeof value === "number" && Number.isFinite(value);
}
JavaScript

この関数は、次のように振る舞います。

isFiniteNumber(10);        // true
isFiniteNumber(-3.14);     // true
isFiniteNumber(NaN);       // false
isFiniteNumber(Infinity);  // false
isFiniteNumber("10");      // false
JavaScript

ここでの重要ポイントは二つです。

「型が number であること」を確認している
「Number.isFinite で NaN / Infinity を除外している」

これで、「本当に“まともな数値”だけを通すフィルター」ができます。


文字列も含めて「数値として安全か」を判定する

入力や API レスポンス向けの isFiniteNumeric

実務では、数値が文字列で来ることが多いので、
number だけでなく string も受け付ける判定関数があると便利です。

function isFiniteNumeric(value) {
  if (typeof value === "number") {
    return Number.isFinite(value);
  }

  if (typeof value === "string") {
    if (value.trim() === "") return false;

    const n = Number(value);
    return Number.isFinite(n);
  }

  return false;
}
JavaScript

動きはこんな感じです。

isFiniteNumeric(10);         // true
isFiniteNumeric("10");       // true
isFiniteNumeric("10.5");     // true
isFiniteNumeric("abc");      // false
isFiniteNumeric("");         // false
isFiniteNumeric("Infinity"); // false
isFiniteNumeric(Infinity);   // false
JavaScript

ここでの深掘りポイントは、「空文字や変換不能な文字列をきちんと弾いている」ことです。
これを通過した値だけを計算に使う、というルールにすると、数値計算がかなり安全になります。


安全な四則演算ユーティリティを作る

足し算:safeAdd

まずは足し算の安全版を作ってみます。

function safeAdd(a, b, defaultValue = 0) {
  if (!isFiniteNumeric(a) || !isFiniteNumeric(b)) {
    return defaultValue;
  }

  const x = Number(a);
  const y = Number(b);
  const result = x + y;

  return Number.isFinite(result) ? result : defaultValue;
}
JavaScript

使い方の例です。

safeAdd(10, 5);          // 15
safeAdd("10", "5");      // 15
safeAdd("10", "abc");    // 0(defaultValue)
safeAdd(10, Infinity);   // 0(defaultValue)
JavaScript

ここでやっていることは、

計算前に「両方が有限数として解釈できるか」をチェック
数値に変換してから計算
結果が Infinity などになっていないかを再チェック

という三段構えです。

割り算:safeDivide

割り算は特に危険なので、専用の安全版を用意する価値があります。

function safeDivide(a, b, defaultValue = 0) {
  if (!isFiniteNumeric(a) || !isFiniteNumeric(b)) {
    return defaultValue;
  }

  const x = Number(a);
  const y = Number(b);

  if (y === 0) {
    return defaultValue;
  }

  const result = x / y;
  return Number.isFinite(result) ? result : defaultValue;
}
JavaScript

使い方です。

safeDivide(10, 2);        // 5
safeDivide("10", "2");    // 5
safeDivide(10, 0);        // 0(defaultValue)
safeDivide("abc", 2);     // 0(defaultValue)
JavaScript

ここでの重要ポイントは、「0 で割る前にチェックしている」ことです。
これにより、Infinity をそもそも発生させない設計になっています。


計算結果の「範囲」までチェックする

業務ルールを組み込んだ安全計算

実務では、「数値として正しいか」だけでなく、
「業務的にあり得る範囲か」も重要になります。

例えば、「割引率は 0〜100 の間だけ許可したい」というケースなら、
次のようなユーティリティが考えられます。

function safePercentage(value, defaultValue = 0) {
  if (!isFiniteNumeric(value)) return defaultValue;

  const n = Number(value);
  if (n < 0 || n > 100) return defaultValue;

  return n;
}
JavaScript

使い方です。

safePercentage("10");   // 10
safePercentage(150);    // 0(defaultValue)
safePercentage("abc");  // 0
JavaScript

「安全な数値計算」は、
単に NaN や Infinity を避けるだけでなく、
こうした「業務ルールを満たす数値だけを通す」ことまで含めて設計すると、かなり強くなります。


実務での具体的な利用イメージ

API レスポンスから金額を扱う

外部 API から金額が文字列で返ってくるケースを考えます。

const response = {
  amount: "1000",
  taxRate: "10",
};
JavaScript

これをそのまま計算に使うのではなく、安全な数値変換+計算を挟みます。

function calcTotalAmount(amount, taxRate) {
  if (!isFiniteNumeric(amount) || !isFiniteNumeric(taxRate)) {
    return 0;
  }

  const a = Number(amount);
  const r = Number(taxRate);

  const tax = safeMultiply(a, r / 100);
  return safeAdd(a, tax);
}
JavaScript

safeMultiplysafeAdd と同じパターンで作れます。

こうしておけば、「API が変な値を返してきても、合計金額が NaN になる」ことは避けられます。

フォーム入力からページング計算をする

ページ番号やページサイズをユーザー入力から受け取るケースも典型的です。

function normalizePaging(input) {
  const page = isFiniteNumeric(input.page) ? Number(input.page) : 1;
  const pageSize = isFiniteNumeric(input.pageSize) ? Number(input.pageSize) : 20;

  const safePage = page < 1 ? 1 : page;
  const safePageSize = pageSize <= 0 ? 20 : pageSize;

  return { page: safePage, pageSize: safePageSize };
}
JavaScript

ここでも、「数値として妥当か」「範囲として妥当か」を両方見ています。
これを通過した値だけを使って計算すれば、ページングまわりのバグはかなり減ります。


小さな練習で感覚をつかむ

次のような値の組み合わせを用意して、自分で isFiniteNumeric, safeAdd, safeDivide を実装して試してみてください。

10 と 5
"10" と "5"
"10" と "abc"
10 と 0
Infinity と 1
"Infinity" と "1"

それぞれについて、「素の演算子で計算した結果」と「安全版ユーティリティで計算した結果」を比べてみると、
「どこで NaN や Infinity が出るのか」「安全版がどう防いでいるのか」がはっきり見えてきます。

そこまで体感できたら、あとはあなたのプロジェクトの中で、

「ここ、たまに NaN が紛れ込んでバグるんだよな」
「この計算、0 で割る可能性あるよな」

という場所に、少しずつ「安全な数値計算ユーティリティ」を差し込んでいけば、
数値まわりのバグはかなり減っていきます。

タイトルとURLをコピーしました