Repository: TypeScript
Issue: Add a --strictNaNChecks option, and a NaN / integer / float type to avoid runtime NaN errors
Labels: Suggestion,In Discussion,Domain: Literal Types

*I have read the FAQ and looked for duplicate issues.*

## Search Terms

* NaN
* NaN type
* Integer type

## Related Issues

* [#21279: strictNullChecks safeguards against null and undefined, but not NaN ](https://github.com/Microsoft/TypeScript/issues/21279)
* [#15135: NaN, Infinity and -Infinity not accepted in number literal types](https://github.com/Microsoft/TypeScript/issues/15135)
  * [#15351: Infinity/NaN literal type (dup)](https://github.com/Microsoft/TypeScript/issues/15351)
* [#195: Suggestion: int type](https://github.com/Microsoft/TypeScript/issues/195)
* [#4639: Proposal: int types](https://github.com/Microsoft/TypeScript/issues/4639)
  * BigInt is scheduled for TS 3.0 - [#15096 - Support for TC39 "BigInt: Arbitrary precision integers in JavaScript" proposal](https://github.com/Microsoft/TypeScript/issues/15096)

## Suggestion

`NaN` has been a big source of errors in my code. I was under the impression that TypeScript (and Flow) could help to prevent these errors, but this is not really true.

TypeScript can prevent *some* `NaN` errors, because you cannot add a number to an object, for example. But there are many math operations that can return `NaN`. These `NaN` values often propagate through the code silently and crash in some random place that was expecting an integer or a float. It can be extremely difficult to backtrack through the code and try to figure out where the `NaN` came from.

I would like TypeScript to provide a better way of preventing runtime `NaN` errors, by ensuring that an unhandled `NaN` value cannot propagate throughout the code. This would be a compile-time check in TypeScript. Other solutions might be a run-time check added with a Babel plugin, or a way for JS engines to throw an error instead of returning `NaN` (but these are outside the scope of this issue.)

## Use Cases / Examples

```js
const testFunction = (a: number, b: number) => {
  if (a > b) {
    return;
  } else if (a < b) {
    return;
  } else if (a === b) {
    return;
  } else {
    throw new Error("Unreachable code");
  }
}

testFunction(1, 2);

testFunction(1, 0 / 0);

testFunction(1, Math.log(-1));

testFunction(1, Math.sqrt(-2));

testFunction(1, Math.pow(99999999, 99999999));

testFunction(1, parseFloat('string'));
```

A programmer might assume that the `Unreachable code` error could never be thrown, because the conditions appear to be exhaustive, and the types of `a` and `b` are `number`. It is very easy to forget that `NaN` breaks all the rules of comparison and equality checks.

It would be really helpful if TypeScript could warn about the possibility of `NaN` with a more fine-grained type system, so that the programmer was forced to handle these cases.

### Possible Solutions 

TypeScript could add a `--strictNaNChecks` option. To implement this, I think TS might need to add some more fine-grained number types that can be used to exclude `NaN`. The return types of built-in JavaScript functions and operations would be updated to show which functions can return `NaN`, and which ones can never return `NaN`. A call to `!isNaN(a)` would narrow down the type and remove the possibility of `NaN`.

Here are some possible types that would make this possible:

```ts
type integer
type float
type NaN
type Infinity

type number = integer | float | NaN | Infinity   // Backwards compatible
type realNumber = integer | float   // NaN and Infinity are not valid values
```
(I don't know if `realNumber` is a good name, but hopefully it gets the point across.)

Here are some examples of what this new type system might look like:

```ts
const testFunction = (a: integer, b: integer) => {
  if (a > b || a < b || a === b) {
    return;
  } else {
    throw new Error("Unreachable code");
  }
}

// Ok
testFunction(1, 2);

// Type error. TypeScript knows that a division might produce a NaN or a float
testFunction(1, 0 / 0);

const a: integer = 1;
const b: integer = 0;

const c = a + b;  // inferred type is `integer`. Adding two integers cannot produce NaN or Infinity.
testFunction(1, c); // Ok

const d = a / b;   // inferred type is `number`, which includes NaN and Infinity.
testFunction(1, d); // Type error (number is not integer)

const e = -2;   // integer
const f = Math.sqrt(e);    // inferred type is: integer | float | NaN    (sqrt of an integer cannot return Infinity)

const g: number = 2; 
const h = Math.sqrt(g);    // inferred type is number (sqrt of Infinity is Infinity)

testFunction(1, h);  // Type error. `number` is not compatible with `integer`.

if (!isNaN(h)) {
  // The type of h has been narrowed down to integer | float | Infinity
  testFunction(1, h);  // Still a type error. integer | float | Infinity is not compatible with integer.
}

if (Number.isInteger(h)) {
  // The type of h has been narrowed down to integer
  testFunction(1, h);  // Ok
}
```

When the `--strictNaNChecks` option is disabled (default), then the `integer` and `float` types would also include `NaN` and `Infinity`:

```ts
type integer      // Integers plus NaN and Infinity
type float        // Floats plus NaN and Infinity

type number = integer | float    // Backwards compatible
type realNumber = number         // Just an alias, for forwards-compatibility.
```

I would personally be in favor of making this the default behavior, because `NaN` errors have caused me a lot of pain in the past. They even made me lose trust in the type system, because I didn't realize that it was still possible to run into them. I would really love to prevent errors like this at compile-time:

<img width="446" alt="screen shot 2018-11-27 at 4 35 34 pm" src="https://user-images.githubusercontent.com/139536/49078834-aba8e900-f271-11e8-8bbe-15ae4ea7f930.png">

This error is from a fully-typed Flow app, although I'm switching to TypeScript for any future projects. It's one of the very few crashes that I've seen in my app, but I just gave up because I have no idea where it was coming from. I actually thought it was a bug in Flow, but now I understand that type checking didn't protect me against `NaN` errors. It would be really awesome if it did! 

(Sorry for the Flow example, but this is a real-world example where a `NaN` type check would have saved me a huge amount of time.)

## Number Literal Types

It would be annoying if you had to call `isNaN()` after every division. When the programmer calls `a / 2`, there is no need to warn about `NaN` (unless `a` is a `number` type that could potentially be `NaN`.) `NaN` is only possible for `0 / 0`. So if either the dividend or the divisor are non-zero numbers, then the `NaN` type can be excluded in the return type. And actually zero can be excluded as well, if both dividend and divisor are non-zero.

Maybe this can be done with the `Exclude` [conditional type](https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types)? Something like:

```ts
type nonZeroNumber = Exclude<number, 0>
type nonZeroRealNumber = Exclude<realNumber, 0>
type nonZeroInteger = Exclude<integer, 0>
type nonZeroFloat = Exclude<float, 0>
```

If the dividend and divisor type both match `nonZeroInteger`, then the return type would be `nonZeroFloat`. So you could test any numeric literal types against these non-zero types. e.g.:

```
const a = 2;     // Type is numeric literal "2"

// "a" matches the "nonZeroInteger" type, so the return type is "nonZeroFloat" 
// (this excludes Infinity as well.)
// (Technically it could be "nonZeroInteger", or even "2" if TypeScript did 
// constant propagation. But that's way outside the scope of this issue.)
const b = 4 / a;
```

## Checklist

My suggestion meets these guidelines:

* [X] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [X] This wouldn't change the runtime behavior of existing JavaScript code
* [X] This could be implemented without emitting different JS based on the types of the expressions
* [X] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
* [X] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).