Typescript: Built-in functions (e.g. isFinite) type guard and accepting null | undefined (strictNullChecks)

Created on 29 Jul 2016  ·  11Comments  ·  Source: microsoft/TypeScript

TypeScript Version: 2.0.0

Code

// --strictNullChecks
interface IExample {
  value?: number;
}

let example: IExample = {};
if (isFinite(example.value)) { // Accept possibly `undefined`
  let a = example.value; // Narrow to `number`
}


Expected behavior:

  • isFinite should accept number | null | undefined rather than strictly number.
  • isFinite should act as a type guard.

Actual behavior:
Won't compile because isFinite is not accepting undefined and is not a type guard.

Proposal:

Change:

declare function isFinite(number: number): boolean;

To:

declare function isFinite(number: number | null | undefined): number is number;

I'm pretty sure there are other functions that could use the same love.
I don't mind putting in some hours if this is an accepted direction to take. Let me know :)

Bug lib.d.ts help wanted

Most helpful comment

isFinite(null) actually returns true (null gets coerced to 0)

JavaScript (╯°□°)╯︵ ┻━┻

It's probably best to _not_ allow null, then?

All 11 comments

Be sure to read contributing.md for instructions on how to make lib.d.ts changes

isFinite(null) actually returns true (null gets coerced to 0)

isFinite(null) actually returns true (null gets coerced to 0)

JavaScript (╯°□°)╯︵ ┻━┻

It's probably best to _not_ allow null, then?

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/isFinite

isFinite('0') also returns true. Number.isFinite('0') returns false, but is only available in ES6 land.

Since these functions can take any type for input, I'd like to see the parameter type changed to any.

What are people passing a string to isFinite attempting to determine about that string?

For isFinite, I suppose the developer is trying to determine if the input can be coerced into a finite number.

For Number.isFinite, I am reading in an any and doing a runtime check to ensure the value is a finite number, so not NaN, Infinity, '0' or null.

any is the accurate representation of the spec. but we opted into making strict to catch cases more error cases, that otherwise would be masked by coercion, e.g. isFinite(getNumber) instead if isFinite(getNumber()). I suppose changing the type to number|string|null|undefined would be safer.

It would also be useful if it returned a type predicate.

isFinite(number: number|string|null|undefined): number is number;

@athasach it can't do that, because it coerces its argument. All isFinite (the global) tells you is if the coerced version of the argument parsed to a finite value; isFinite("30") is true but "30" is not a number

@RyanCavanaugh I should have specified Number.isFinite, which doesn’t coerce the value as far as I know.

Bumping this. According to the spec, Number.isFinite and similar Number do not throw, nor do they specify type constraints.
https://www.ecma-international.org/ecma-262/10.0/index.html#sec-number.isfinite

If Type(number) is not Number, return false.
If number is NaN, +∞, or -∞, return false.
Otherwise, return true.

That's it. Very simple. There are is no reason for this and similar functions to be constrained as such, since these constraints do not follow the spec itself. This isn't a huge deal as it's easy to extend the types, e.g.

interface NumberConstructor {
  isFinite(v?: any): v is number
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jbondc picture jbondc  ·  3Comments

Antony-Jones picture Antony-Jones  ·  3Comments

bgrieder picture bgrieder  ·  3Comments

weswigham picture weswigham  ·  3Comments

blendsdk picture blendsdk  ·  3Comments