LoginSignup
2
1

More than 1 year has passed since last update.

TypeScript(JavaScript)における数値チェック(isNaN)をeslintを使ってより安全に使う

Last updated at Posted at 2022-08-31

はじめに

以前に「TypeScriptにおけるnullとundefinedそしてfalsy値」という記事を書いた際に、
NaN === NaNがfalseになるという仕様を教えていただきました。
熟練者であれば当然の仕様かと思いますが、改めてまとめていきたいと思います。

結論

TypeScript(JavaScript)で数値チェックをするときはNumber.isNaN()を使いましょう。

const hoge = なにか;
if (Number.isNaN(hoge)) { console.log('数値ではありません'); }

eslintでisNaN()を使えないように設定しよう

eslintrc.yml
rules: {
  no-restricted-globals: [
    "error",
    {
      "name": "isNaN",
      "message": "Use Number.isNaN"
    },
  ]
}

NaNとは

JavaScriptのドキュメント(以下MDN)によるとこのように記載されています。

グローバルプロパティ NaN は非数 (Not-A-Number) を表す値です。

Numberではないとのことですがtypeofで出力してみるとnumber型だということになります。

console.log(typeof NaN); // number

MDNではこのように表記されています。

・数値が解釈できない (例えば parseInt("blabla") または Number(undefined))
・結果が実数にならない数学演算 (例えば Math.sqrt(-1))
・オペランドが NaN である (例えば 7 ** NaN)
・不確定形 (例えば 0 * Infinity または undefined + undefined)
・文字列が関わる加算以外の何らかの演算 (例えば "foo" / 3)

このことから、NaNはnumber型ではないという意味ではなく、 数値としてあらわすことのできない数字 ということになります。

NaNを判別するにはNumber.isNaN()を使いましょう

NaN === NaN はfalseになる

NaNはいかなる値と比較してもfalseになります。そのため別の比較方法が求められます。

console.log(NaN === NaN); // false

isNaN()だとNaNかどうかを厳密に判断できない

isNaN()undefined'hoge'などでもnumber型に変換されtrueが返ってきてしまい、厳密にNaNかどうかを判断することができません。

isNaN(undefined); // true
isNaN('hoge');    // true

Number.isNaN()であれば厳密に判断できる

Number.isNaN()number型であり、かつNaNである時にのみtrueを返します。

Number.isNaN(undefined); // false
Number.isNaN('hoge');    // false

注意するべき点

指数表記のeが数値扱いになる

10のべき乗をe + nのように表記することがあり、一見数値ではないのですが、れっきとした数値の為、
TypeScript(JavaScript)ではnumber型の数値になります。

console.log(typeof 1e5); // number

0 / 0 もNumber.isNaN()ではtrueになる

当然ですが、0 ÷ 0もNaNになります。
また、n ÷ 0(nは0以外)の場合は(±)infinityになります。

console.log(Number.isNaN(0 / 0)); // true

Number.isNaN()の代替()ポリフィル

MDNで紹介されているポリフィル

Number.isNaN()es6からの機能のため、古いブラウザなどでは動作しない可能性があります。
そのため、Number.isNaN()がなかった頃のイディオムとして以下のものが使用されていました。
NaNはJavaScriptで唯一自分自身と比較して等しくない値という特性を利用したものです。

Number.isNaN = Number.isNaN || function isNaN(input) {
    return typeof input === 'number' && input !== input;
}

正規表現による数値チェック

こちらで紹介されていた方法です。
自分がチェックしたい内容に合わせてカスタムして使えるので場面によっては使えると思います。
その中でNumber.isNaN()に近いものを一つ紹介させていただきます。

function isNumber(numVal){
  // チェック条件パターン
  var pattern = /^[-]?([1-9]\d*|0)(\.\d+)?$/;
  // 数値チェック
  return pattern.test(numVal);
}

eslintを利用してisNaNを禁止したい

このようにisNaN()はいくつかバグを引き起こす可能性があるので、禁止したいのですが、
プログラマが使わないようにと気を付けても使ってしまうものです。こういう時はeslintを使って禁止にしちゃいます。

前提

・node.js
・eslint
上記二つを導入済み

eslintrcファイルは本記事ではymlですが、ご自身の環境によって書き方を変えていただければと思います。

NaNを直接比較できないように

何かしらの有名スタイルを継承しているのであれば設定をする必要は無いですが
一応こちらの設定があるか確認しましょう。なければ追加します。

eslintrc.yml
rules: {
  use-isnan: "error"
}

isNaNが使えないように

この設定でisNaNを使えないようにします。

eslintrc.yml
rules: {
  no-restricted-globals: [
    "error",
    {
      "name": "isNaN",
      "message": "Use Number.isNaN"
    },
  ]
}

このようにisNaN()を使用したときにerrorが出るようになりました。

hoge.ts
const hoge = なにか;
console.log(isNaN(hoge));        // Unexpected use of 'isNaN'. Use Number.isNaN
console.log(Number.isNaN(hoge)); // 問題なし

さいごに

他にも良い方法やご指摘などありましたら、コメントやリクエストをしていただければと思います。

参考

JavaScript の数値チェックに isNaN を使ってはいけない理由
正規表現を用いて JavaScript で数値チェックを行う方法
MDN isNaN()
MDN Number.isNaN()
eslint use-isnan
eslint no-restricted-globals

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1