This site has been updated.
Reload to display the latest version?
(click here)

Test whether variable is a number

July 18, 2020

The post title “Test whether variable is a number” looks like a trivial question, but let’s dig deeper and try to cover all edge cases. I will split it into two problems and will cover them separately.

isNumber

First I’ll start with the simple problem. You have variable that may or may not be a number. Write a function isNumber() that will return true if it is in fact ‘number’ or false otherwise.

Here I only need to check the type of given variable. In this situation I have two edge cases.

The first one is NaN, which has a number type, but is not a meaningful number, since I can’t use it in any calculation. And it’s up to the developer to decide how to handle this situation. By the way lodash/isNumber is not covering NaN case and will return true for NaN. Which is technically correct, but still I prefer not to call it a number.

The second edge case is not frequent, but since JS allows it, we will cover it as well. User has additional way to define a number, by using Number as a constructor, like this:

const num = new Number(2);

In this case the type of num variable will be an object, when in fact it will act as a number in all other cases. So we need to check for that as well.

function isNumber(value) {
    // `isNaN` is build in global function, that is supported since IE6
    // (Since ES6, we also have `Number.isNaN` method which make more since, then global function)
    if (isNaN(value)) {
        return false;
    }
    if (value instanceof Number) {
        return true;
    }
    return typeof value === 'number';
}

Ok, now let’s see the test cases:

console.assert(isNumber(2) === true, 'should validate regular number');
console.assert(isNumber('true') === false, 'should validate string');
console.assert(isNumber('7') === false, 'should validate string as number');
console.assert(isNumber(NaN) === false, 'should validate NaN');
console.assert(isNumber(Number(10)) === true, 'should validate Number');
console.assert(isNumber(new Number(5)) === true, 'should validate Number constructor');
console.assert(isNumber(true) === false, 'should validate boolean');

couldBeNumber

Now let’s solve a more complicated case, that is related to types magic of JS.

You have a variable that may or may not be a number, but it can be converted to a number in the meaningful way. For example string ‘2’ can be converted to number 2, but boolean true cannot. Write a function couldBeNumber() that will return true if it is in fact number or false otherwise.

function couldBeNumber(value) {
    // First lets check whether given value is a string.
    // Here I'm also covering the case when value defined via constructor `new String()`.
    const isString = typeof value === 'string' || value instanceof String;
    // If the user gave us a string, then we need to check whether it's empty.
    // Since string can be created with `String` constructor I need to cast it to the regular string,
    // therefore I'm doing `value + ''` (otherwise I can't compare it to an empty string).
    // By the way, I need to check for an empty string, since `Number('')` will return `0`.
    // And I'm using `Number` in the first place since this way I can cover integers
    // and floats with one conversion method.
    if (isString && value + '' !== '') {
        // `Number()` of something that can't be converted to number will return `NaN` and
        // by specification of ECMAScript `NaN` not equal to `NaN`.
        // Therefore I can just compare it to itself.
        return Number(value) === Number(value);
    }
    // In the end I can just use the previously defined `isNumber()` function.
    return isNumber(value);
}

Now let’s see the test cases. Half of them are covered in the first part of this post, but I prefer to copy them over here as well.

console.assert(couldBeNumber('1') === true, 'should validate string as a number (integer)');
console.assert(couldBeNumber('1.2') === true, 'should validate string as a number (float)');
console.assert(couldBeNumber('-1.2') === true, 'should validate string as a number (negative float)');
console.assert(couldBeNumber(new String('5')) === true, 'should validate string as a number (String constructor)');
console.assert(couldBeNumber('') === false, 'should validate empty string');
console.assert(couldBeNumber('a') === false, 'should validate string');
console.assert(couldBeNumber(2) === true, 'should validate regular number');
console.assert(couldBeNumber(NaN) === false, 'should validate NaN');
console.assert(couldBeNumber(Number(10)) === true, 'should validate Number');
console.assert(couldBeNumber(new Number(5)) === true, 'should validate Number constructor');
console.assert(couldBeNumber(true) === false, 'should validate boolean');