かもメモ

自分の落ちた落とし穴に何度も落ちる人のメモ帳

JavaScript 数値かどうか判定したい

JavaScript には isNumber というメソッドなく、整数かどうか判定する Number.isInteger() と有限数かどうか判定する Number.isFinite(), isFinite() だけなので、数値かどうか判定したい時は独自実装するかライブラリをつかうしか無いのですが暗黙の型変換があるので意外とハマりどころがあります。( TypeScript 使え!ってのは無しで、

結論から書けば、typeofnumber なもので、NaNInfinity を除外すれば有限数な数値かどうか判定できる。

const isNumber = (n) => {
  return typeof(n) === 'number' && n - n === 0;
};

of

Number.isFinite(n);

暗黙の型変換のヤバイやつ

JavaScript で簡易的に数値に変換する方法として - 0 する方法があります。

"10" - 0; // => 10
"a" - 0; // => NaN

これを利用した数値かどうか判定するような関数

const isNumber = (n) => {
  const v = n - 0;
  if ( v || v === 0 ) {
    return true;
  }
  return false;
};

渡される値がある程度決まっている場合は問題ないのですが、特定の値の時意図しない動作になってしまう事があります

✅isNumber( 1 ); // true
✅isNumber( -1 ); // true
✅isNumber( 10.1 ); // true
✅isNumber( -1.1 ); // true
✅isNumber( 5e3 ); // true
✅isNumber( 10e+3 ); // true
✅isNumber( 10e-3 ); // true
✅isNumber( 0xff ); // true
✅isNumber( Math.PI ); // true
✅isNumber( parseInt('012') ); // true
✅isNumber( parseInt('012.5') ); // true
✅isNumber( Infinity ); // true
✅isNumber( -Infinity ); // true
✅isNumber( '10' ); // true
✅isNumber( '-10' ); // true

✅isNumber( NaN ); // false
⛔️isNumber( null ); // true
✅isNumber( undefined ); // false
⛔️isNumber( true ); // true
⛔️isNumber( false ); // true
⛔️isNumber( '' ); // true
⛔️isNumber( ' ' ); // true
✅isNumber( 'string' ); // false
⛔️isNumber( [] ); // true
⛔️isNumber( [1] ); // true
✅isNumber( {} ); // false
✅isNumber( {a: 1} ); // false
✅isNumber( function() {} ); // false

これは - 0 で数値化する際に暗黙の型変換で数値に変換されてしまう値があるためです

null - 0; //=> 0
true - 0; // => 1
false - 0; // => 0
"" - 0; // => 0
" " - 0; // => 0
[] - 0; // => 0
[1] - 0; //=> 1

数値かどうか判定する

数値でかつ NaN, Infinity は除外したい

コード

const isNumber = (n) => {
  if ( typeof(n) === 'number' && Number.isFinite(n) ) {
    return true;
  }
  return false;
};

or

const isNumber = (n) => {
  if ( typeof(n) === 'number' && n - n === 0 ) {
    return true;
  }
  return false;
};

👇 test

✅ isNumber( 0 ); // true
✅ isNumber( 1 ); // true
✅ isNumber( -1 ); // true
✅ isNumber( 10.1 ); // true
✅ isNumber( -1.1 ); // true
✅ isNumber( 5e3 ); // true
✅ isNumber( 10e+3 ); // true
✅ isNumber( 10e-3 ); // true
✅ isNumber( 0xff ); // true
✅ isNumber( Math.PI ); // true
✅ isNumber( parseInt('012') ); // true
✅ isNumber( parseFloat('012.5') ); // true

✅ isNumber( Infinity ); // false
✅ isNumber( -Infinity ); // false
✅ isNumber( NaN ); // false
✅ isNumber( null ); // false
✅ isNumber( undefined ); // false
✅ isNumber( true ); // false
✅ isNumber( false ); // false
✅ isNumber( "10" ); // false
✅ isNumber( "-10" ); // false
✅ isNumber( "" ); // false
✅ isNumber( " " ); // false
✅ isNumber( "string" ); // false
✅ isNumber( [] ); // false
✅ isNumber( [ 1 ] ); // false
✅ isNumber( {} ); // false
✅ isNumber( { a: 1 } ); // false
✅ isNumber( function() {} ); // false

文字列の数字も true にする

先の数値判定に文字列で数字になるものを true にする判定を追加すればOK

const isNumberAllowString = (n) => {
  const type = typeof(n);
  if ( type === 'number' && Number.isFinite(n) ) {
    return true;
  }
  if ( type === 'string' && n.trim() !== '' && Number.isFinite(n - 0) ) {
    return true;
  }
  return false;
};

👇 test

✅ isNumberAllowString( 0 ); // true
✅ isNumberAllowString( 1 ); // true
✅ isNumberAllowString( -1 ); // true
✅ isNumberAllowString( 10.1 ); // true
✅ isNumberAllowString( -1.1 ); // true
✅ isNumberAllowString( 5e3 ); // true
✅ isNumberAllowString( 10e+3 ); // true
✅ isNumberAllowString( 10e-3 ); // true
✅ isNumberAllowString( 0xff ); // true
✅ isNumberAllowString( Math.PI ); // true
✅ isNumberAllowString( parseInt('012') ); // true
✅ isNumberAllowString( parseFloat('012.5') ); // true
✅ isNumberAllowString( "10" ); // false
✅ isNumberAllowString( "-10" ); // false

✅ isNumberAllowString( Infinity ); // false
✅ isNumberAllowString( -Infinity ); // false
✅ isNumberAllowString( NaN ); // false
✅ isNumberAllowString( null ); // false
✅ isNumberAllowString( undefined ); // false
✅ isNumberAllowString( true ); // false
✅ isNumberAllowString( false ); // false
✅ isNumberAllowString( "" ); // false
✅ isNumberAllowString( " " ); // false
✅ isNumberAllowString( "string" ); // false
✅ isNumberAllowString( [] ); // false
✅ isNumberAllowString( [ 1 ] ); // false
✅ isNumberAllowString( {} ); // false
✅ isNumberAllowString( { a: 1 } ); // false
✅ isNumberAllowString( function() {} ); // false

解説的なもの

typeof で判定

typeof()number がになるもので判定すると先の暗黙の変換で数値になるものを除外することができる

typeof( null ); // => "object"
typeof( true ); // => "boolean"
typeof( false ); // => "boolean"
typeof( "" ); // => "string"
typeof( " " ); // => "string"
typeof( [] ); // => "object"
typeof( [1] ); // => "object"
NaN, Infinity を除外する

NaN, Infinitynumber なので別の条件で除外する

isFinite

isFinite
渡された値が有限数かどうかを判定します。
cf. isFinite() - JavaScript | MDN

JavaScript には isFiniteNumber.isFinite がある。

  • isFinite … 暗黙の型変換が行われる
  • Number.isFinite … 暗黙の型変換を行わない
    IEでは使えない cf. Number: isFinite | Can I use

e.g.

Number.isFinite( Infinity ); // => false
Number.isFinite( -Infinity ); // => false
Number.isFinite( NaN ); // => false
同値の引き算を利用する

同値で引き算をすると有効数だと 0 になるが、Infinity, NaNNaN になることを利用して除外する

Infinity - Infinity; // => NaN
-Infinity - (-Infinity); // => NaN
NaN - NaN ; // => NaN
文字列の数字を判定

string - 0 で数値化して、 isFinite()NaNInfinity になるものを弾く

"a" - 0; // => NaN
"Infinity" - 0; // => Infinity
"-Infinity" - 0; // => Infinity

ただし空文字列だけは 0 になってしまうので別途除外する必要がある

"" - 0; // => 0;
"   " - 0; // => 0;

"".trim(); // => ""
"   ".trim(); // => ""

所感

文字列の数字も許可したい場合は、is-number というライブラリを使うのが楽だと思います。


[参考]

入門Node.jsプログラミング

入門Node.jsプログラミング

  • 作者:Jonathan Wexler
  • 出版社/メーカー: 翔泳社
  • 発売日: 2019/09/25
  • メディア: 単行本(ソフトカバー)