98

I work with WebStorm 2016.2.2, TypeScript 2.1, Node.js.

For some reason, isNaN is declared as a function that only accepts a number:

declare function isNaN(number: number): boolean;

I tried to change it to any, but it looks like it doesn't influence on the TSC. I still get the same error:

Argument of type 'string' is not assignable to parameter of type 'number'

My code (simplified):

isNaN("10");

How can I solve/workaround it?


Edit:

Notice that according to specification, isNaN's parameter can be any type: Number.isNaN()

Also: My code was simplified. I actually receive a parameter that may be either a string or a number, and if it's a string it may be either a stringy number that I would like to convert to number ("10") or a simple string ("Hello world").

I didn't want to make this question long by including my entire code, but because it caused confusion, this is my real code:

            if (typeof expectedValue === "string" && !isNaN(expectedValue)) {
                    expectedValue = +expectedValue;
                }

            if (typeof actualValue === "string" && !isNaN(ctualValue)) {
                actualValue = +actualValue;
            }

            switch (this.operator) {
                case Operator.equal:
                    return actualValue == expectedValue;
                case Operator.notEqual:
                    return actualValue === undefined || actualValue != expectedValue;
                case Operator.greaterThan:
                    return actualValue > expectedValue;
                case Operator.littleThan:
                    return actualValue < expectedValue;
                case Operator.greaterOrEqual:
                    return actualValue >= expectedValue;
                case Operator.littleOrEqual:
                    return actualValue <= expectedValue;
            }
Alon
  • 10,381
  • 23
  • 88
  • 152
  • (1) Why would you need to check if a string is not a number? It's pretty clear that it's not. (2) How did you try to change it? – Nitzan Tomer Feb 08 '17 at 17:43
  • 3
    You typically are going to pass an expression to `isNaN()` to see if the expression results in a number or not. Passing a literal is a bit unnecessary, don't you think? – Scott Marcus Feb 08 '17 at 17:45
  • 1
    @NitzanTomer I don't really check if a literal is Nan, I simplified my code. Anyway isNan is supposed to accept any type according to the specification. – Alon Feb 08 '17 at 18:10
  • @ScottMarcus I don't really check if a literal is Nan, I simplified my code. Anyway isNan is supposed to accept any type according to the specification. – Alon Feb 08 '17 at 18:11
  • But if you're using typescript then how do you find yourself in a situation where you pass it anything but a `number` or `any`? – Nitzan Tomer Feb 08 '17 at 18:12
  • @NitzanTomer I try to pass an any! But it only accepts a number. This is the problem. The value can be 3 or "3" or "hello world". – Alon Feb 08 '17 at 18:14
  • It does work with `any`: `let a: any = "str"; console.log(isNaN(a));` is fine with the compiler, in fact this is fine as well: `isNaN("str" as any)` – Nitzan Tomer Feb 08 '17 at 18:15
  • @NitzanTomer you're right. I tested it now and it works with any. The problem was that my parameter was declared as "string | number". For next time I'll write my actual code instead of simplifying it. Please write it as an answer (to pass any) and I'll accept it. – Alon Feb 08 '17 at 18:24
  • 1
    @NitzanTomer The same way you find yourself calling parseInt and parseFloat, which explicitly require a string. Sometimes your data is not in its correct format/type until you put it there. – Daniel May 20 '21 at 04:03

8 Answers8

91

I advise you to implement your code differently.
The reasons:

  1. It might be short, but it's not easy to understand what's going on
  2. Using isNaN isn't the best option here: isNaN("") returns false as well

You better try to convert the value into a number and check if that's NaN or not (as @smnbbrv wrote):

if (typeof expectedValue === "string" && !Number.isNaN(Number(expectedValue))) {
    expectedValue = Number(expectedValue);
}

Edit

You can pass your value as any:

isNaN(ctualValue as any)

To bypass the compiler check.

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • 1
    Your question is what's called a [XY problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem), which is why all the answers/comments here are about your use of `isNaN`. I updated my answer for what you wanted – Nitzan Tomer Feb 08 '17 at 19:24
  • Thanks. I changed my code to your suggestion. You're right it was an XY problem. I didn't know that an empty string would return false. – Alon Feb 08 '17 at 19:35
  • I refer to https://stackoverflow.com/a/175787/351426 as to the validity of isNaN taking more than just a number. – Jono Jul 27 '17 at 16:17
  • 2
    1up for `as any`. – Andre Elrico Apr 11 '19 at 12:55
  • 8
    Since `Number("")` is `0`, `isNaN(Number(""))` will *still* return false... – Neil Jun 28 '20 at 11:40
  • Passing the value as `any` helped by my compiler wasn't satisfied. I used `isNaN` to prevent NaN from flashing while the data loaded. Passing `isNaN(value as unknown as number)` was the solution. In my case, `percentageTotal={isNaN(percentageTotal as unknown as number) ? "0" : percentageTotal}` Having a loader, while data loaded could have solved this as well, but now I am free to display empty charts while the data loads for a nice visual effect. Thank you @NitzanTomer for relieving some TS troubles. – tralawar Jun 04 '22 at 00:24
25

You should not solve it because this is how TypeScript works.

Just cast the input to number first

Number("10") // 10
Number("abc") // NaN

and then check the result with the isNan function:

isNaN(Number("abc"))
bukzor
  • 37,539
  • 11
  • 77
  • 111
smnbbrv
  • 23,502
  • 9
  • 78
  • 109
  • 2
    It typically suggested to avoid the `Number()`, `String()` and `Boolean()` functions. Probably better to use `parseInt()` and `parseFloat()`. – Scott Marcus Feb 08 '17 at 17:46
  • 4
    @ScottMarcus you are probably talking about `new Number()` which usually one should avoid (because instead of creating literal they create objects) and is deprecated in ES6 if I remember right; however `Number()` does not have any problem. If it does, please clarify that – smnbbrv Feb 08 '17 at 17:49
  • Actually, I'm talking about their usage altogether. – Scott Marcus Feb 08 '17 at 17:50
  • 2
    Please, clarify why it is bad. I use it often and I don't have any problems. It's not like I'm telling you are wrong; I really want to know if it is a bad practice to improve my code – smnbbrv Feb 08 '17 at 17:51
  • What will `Number('0x11')` and `Number('0b11')` produce? – Scott Marcus Feb 08 '17 at 17:55
  • What will `Number("123hui")` return? – Scott Marcus Feb 08 '17 at 17:56
  • 1
    `NaN` for both which is right behavior unless you explicitly want to support hexadecimal numbers which is not a subject of this discussion. I've got your point: you have no arguments. – smnbbrv Feb 08 '17 at 17:58
  • 1
    Actually, `Number('0x11')` produces `17` and `Number('0b11')` produces `3`. Only `Number("123hui")` produces `NaN`. So, is it correct to get `17` from `Number('0x11')` when the string happens to start with `0x`? Or, should it return `NaN`? – Scott Marcus Feb 08 '17 at 18:00
  • I don't really check if a literal is Nan, I simplified my code. Anyway isNan is supposed to accept any type according to the specification. – Alon Feb 08 '17 at 18:11
  • 2
    @ScottMarcus doesn't `parseInt('0x11')` also return 17? – Paarth Feb 08 '17 at 18:11
  • 3
    @Paarth yes, but you're never meant to use `parseInt` without a `radix` – `parseInt('0x11') // => 17` vs `parseInt('0x11', 10) // => 0`. – Mulan Feb 08 '17 at 18:24
  • 2
    Tho I still disagree that using `Number` is somehow bad. As with all things, you have to consider your use case and determine which function fulfills those specific criterion – if no such built-in is satisfactory, a custom function is required. – Mulan Feb 08 '17 at 18:26
  • 1
    @Paarth `parseInt()` has a second, optional (but really it should always be used) argument to supply a radix, so that the conversion happens in the correct numeric base. – Scott Marcus Feb 08 '17 at 18:31
3

As ironically only numbers can be NaN, you need to transform strings into numbers first.

A very simple way to do this is the unary plus operator.

So you can do a simple isNaN(+"10").

Keep in mind that thing like +"", +" " and +"\n" are 0!

pa1nd
  • 392
  • 3
  • 16
1

First of all, only values of type number can be NaN. So if the static context tells you your value is of type string for example, you can be sure that it is not a NaN. If you have a value with type string|number (which should be avoided btw) you can still decide how you handle this. Strictly speaking, the string value "foo" is not NaN, as NaN is a specific value specified in the IEEE standard for float numbers. But still, in javascript, isNaN("foo") will be true, as the function will coerect the string to a number first, and that coerection results in a NaN. Typescript tries to take advantage of types here, it tries to prevent you from using isNaN where you should not.

Tamas Hegedus
  • 28,755
  • 12
  • 63
  • 97
1

In the accepted answer, !Number.isNaN(Number(expectedValue)) still returns true for empty string ('') and whitespace strings (' '). And converting these to number will result in 0.

I'm not a JavaScript developer, and – especially coming from .Net – it looks insane to me as well, but this is what I've done that seems to work:

private static isNumber(value: any): boolean {
  return (typeof value === 'number' && !isNaN(value))
      || ((typeof value === 'string') && value.trim() != '' && !isNaN(Number(value)))
  }

If you know a saner solution, be sure to edit this!

console.log(isNumber([]));      // false
console.log(isNumber({}));      // false
console.log(isNumber(""));      // false
console.log(isNumber("   "));   // false
console.log(isNumber(" 1  "));  // true <= note
console.log(isNumber(" 1  2")); // false
console.log(isNumber("1"));     // true
console.log(isNumber(1));       // true
Leaky
  • 3,088
  • 2
  • 26
  • 35
1

Passing isNaN(value as unknown as number) satisfied my compiler. In my case, I was using isNaN() to prevent "NaN" from flashing while data loaded. This allowed me to pass a string into isNaN() since the interface expected a string.

tralawar
  • 506
  • 6
  • 12
0

You can solve this by using parseInt inside your isNaN. The check isNaN will still work if the parseInt returns NaN. And your Typescript error will be solved.

if (typeof actualValue === "string" && !isNaN(parseInt(actualValue, 10))) {
  actualValue = +actualValue;
}
Ellaji
  • 9
  • 3
  • I don't like this solution, as it requires "wasting extra CPU cycles", when `isNaN` is in fact perfectly capable of handling non-number types. `isNaN` is not only useful for checking whether the number is "exactly `NaN`", but also whether the object "is not a number". Using `const x = n === undefined || n === null || isNaN(n) ? -1 : n` where `const x = isNaN(n) ? -1 : n` is just a waste of space. – mausworks Nov 22 '19 at 11:51
  • @diemaus And what do you think `isNaN` function does? It converts that non-numeric value to numeric, which is "wasting extra CPU cycles" as well. This was it's just explicit. Explicit = good. – Roberto Zvjerković Feb 12 '20 at 11:54
0

Found this answer from Google, and reached my answer from the various answers and comments here; my answer is to use:

isNaN(Number(string))

This will correctly tell me if the string is a number.

I was previously using parseInt(), but this fails if the string is something like 123abc as parseInt just discards the letters (useful sometimes, but not here).

Note: I'm happy that Number('') evaluates to zero, but if your not, this isn't the solution!

Ben Robinson
  • 1,602
  • 3
  • 16
  • 30