I am creating a function in TypeScript that essentially extends the functionality of the typeof
keyword, including showing the appropriate IntelliSense for the type whenever it is known (via conditional types). I have it working just fine for all my other custom types so they only show IntelliSense for 1 return type when it is known, but I am not sure how to make it work specifically for the NaN and Infinity values. I believe this is because these are being overlooked as "number" literal types. (working example below)
public static async type(value: number): Promise<'number' | 'nan' | 'infinity'> // would rather this only show just 1 type
public static async type(value: []): Promise<'array'>
public static async type(value: Promise<any>): Promise<'promise'>
// and several more...
public static async type(value: any): Promise<T_detectType> {
// do stuff
}
My Question: Is it possible to create a conditional type based on NaN and/or Infinity? (non-working example below)
public static async type(value: NaN): Promise<'nan'>
public static async type(value: Number.POSITIVE_INFINITY | Number.NEGATIVE_INFINITY): Promise<'infinity'>
I am able to give more details as necessary, but I am unsure what you may need and I am trying to keep this question concise.
CodePudding user response:
For better or worse, TypeScript does not have literal types representing NaN
, Infinity
, or -Infinity
. The narrowest type available for NaN
, Infinity
, and -Infinity
is just number
. So, for example, while a const
initialized with a numeric literal will generally have a literal type:
const x = 1.234;
// const x: 1.234
The same does not happen with NaN
or Infinity
:
const y = NaN;
// const y: number
const z = -Infinity;
// const z: number
There's no workaround I can think of for this that isn't significantly worse than just giving up. You could imagine providing some simulated nominal type
type NaN = number & { NaN: true };
type Infinity = number & { Infinity: true };
type NegInfinity = number & { NegInfinity: true };
But then you'd need to use type assertions to use them:
const w = NaN as NaN;
const v = Infinity as Infinity;
const u = -Infinity as NegInfinity;
and if your goal is to write Blah.type(NaN)
and have Promise<'nan'>
come out, you could only achieve that goal by writing Blah.type(NaN as NaN)
which is silly, especially because nothing stops someone from writing Blah.type(123 as NaN)
.
In TypeScript, NaN
, Infinity
, and -Infinity
are just of type number
, and that's that.
As for changing this:
There is a declined feature request at microsoft/TypeScript#15135 asking for such support. From reading that issue (and the linked design discussion at ms/TS#15356) it looks like nobody was able to articulate an important enough use case to support it. Supporting NaN
as a literal might be annoying because, for example, NaN === NaN
is false
.
Personally, I'm inclined to agree that we care about NaN
types because they are falsy, and tracking falsiness is something worthwhile. But in the pull request implementing numeric literal types, a comment by the implementer says that falsiness doesn't matter, and he can't think of any meaningful "scenarios where you'd use NaN
and Infinity
as singleton types, discriminant values, or literal values for overloading."
There is an open issue at microsoft/TypeScript#32277 asking for Infinity
and -Infinity
literal types as opposed to NaN
; it's currently labeled as "awaiting more feedback" which means they want to see comments describing compelling use cases before considering implementing it.
And there's an issue at microsoft/TypeScript#36964 that demonstrates how the lack of consideration of a NaN
-like type causes incorrect narrowings... a falsy number
is assumed to be 0
whereas it should really be 0 | typeof NaN
to be correct. It's also labeled as awaiting feedback, and the requester seems to be saying that it should be number
since typeof NaN
is number
(as opposed to saying that there should be a NaN
type). But it's relevant.
It's just about conceivable that enough people could add comments to those two open issues saying how literal NaN
, Infinity
, and -Infinity
types should be added, and detailing use cases that are persuasive enough to eventually cause this to happen. But it's quite likely that this will not happen. And it seems to me very unlikely that stronger typing of a typeof
helper function is going to be seen as particularly compelling. So while it might not hurt anything to go there and give them a