You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
You actually can get Infinity type if you're clever and other fun floating point oddities #31752
As explained in #15135 and #15351, the Typescript team doesn't intend to support NaN and Infinity literals, due to complexity. Explicitly typing something as Infinity or -Infinity gives the error:
'Infinity' refers to a value, but is being used as a type here.
As it turns out, you actually can get the Infinity type fairly easily. Just use a really large number that parses to infinity as your type, like 1e999. -1e999 gives you negative Infinity. Fortunately, I don't believe you can get NaN from just parsing a double precision literal other than NaN itself. 0/0 is the simplest way to get NaN, but that's 2 literals and a divide.
There are a number of other floating point literal peculiarities resulting from round-off as well. I fully expect the Typescript team to "won't fix" them:
type DoesntExtend = 1.000000000000001 extends 1 ? 'yes' : 'no' // Resolves to 'no'
type Extends = 1.0000000000000001 extends 1 ? 'yes' : 'no' // Resolves to 'yes'
Here's another.
type NegativeZero = -0 extends 0 ? 'yes' : 'no'; // Resolves to 'yes'
In this case, -0 and 0 are actually distinct numbers with different bit patterns. Both Chrome and Firefox dev consoles correctly echo -0 as -0 rather than 0. However, the IEEE-754 standard defines that 0 === -0, so I'm not surprised Typescript thinks they're the same type. The intent for this distinction is to convey the idea of limits as a numeric representation (not to mention floating point is sign-magnitude anyways, so why not embrace it). For example, 1/-Infinity and -8*0 both result in -0 rather than 0. For a type system, I'm not sure that you strictly need or want to maintain this distinction.
TypeScript Version: Repro'd on playground
Search Terms:
Infinity, NaN Code
type Illegal = Infinity; // Doesn't compile
type Inf = 1e999; // Resolves to Infinity
type NegativeInf = -1e999; // Resolves to -Infinity
// THIS CODE IS EVIL AND YOU SHOULD RUN AWAY FROM ITtypeInfinity=1e999;if(!((x: any): x is Infinity=>true)(Infinity))thrownewError();consta: Infinity=Infinity;// okay now
Reacted by Titian Cernicova-Dragomir, Ashlynne Mitchell, Alistair Smith, Han Seoul-Oh, Anurag Hazra, Fatih Altinok, Twilight, Omri Luzon and 정해준
Reacted by Titian Cernicova-Dragomir, Nicholai Main, Soichiro Miki, Ashlynne Mitchell, Alistair Smith, Han Seoul-Oh, Jonah Snider, cobalt, Ricardo Fernández Serrata, Leon Adler and 2 more
Reacted by Ricardo Fernández Serrata and Jimmy Zhening Luo
Reacted by AnyhowStep, Ashlynne Mitchell, Alistair Smith, Anurag Hazra and Ricardo Fernández Serrata
IEEE INF has some really useful properties which aren’t strictly mathematically correct (such as x / 0 = inf or x % inf = x) that I’ve actually found it useful in practice from time to time.
That said—not useful enough to inject that much evilness into my code. 😄
typeNegativeZero=-0extends0 ? 'yes' : 'no';// Resolves to 'yes'
This one is kind of interesting, because technically by the meaning of extends, the conditional type is correct anyway: -0 is indeed substitutable for 0 (as they compare equal). Likewise for the ones with rounding errors.
At the end of the day, floats are goofy and it might be fine to let the type system reflect that except in a few edge cases you really want to manage. There are an infinite number of decimal representations (memory granted) of every non-NaN number, and I don't think you can rightly ordain one as provenance. This means roundoff on types is just going to be a thing.
+- 0 is usually not a useful distinction. Most of the cases I can think about are pedantry. For example, Math.sign returns -0 if you pass -0, so you could type its return value as -1 | -0 | NaN | 0 | 1. Without the -0 type, you might be surprised that at runtime 1 / Math.sign(x) can return -Infinity while the return type led you to believe you had to only check NaN, Infinity, +-1. But this is also moot without a NaN literal type.
I don't know that much about type theory, but I think of the number type as being a very large finite union of one representation of every non-NaN floating point binary sequence (including distinct +0 and -0 and +Infinity and -Infinity) and a single NaN. NaN has many binary representations, but you can't tell them apart in Javascript. This is regardless of what the equality operator does. This looks like:
This is how I mentally reason about floating point numbers and having a type system that matches that would be convenient for reasoning about Typescript numbers. However, 1:1 matching the floating point number line to type literals my not be desirable in a type system for reasons I don't yet understand as I'm not a compiler expert and it probably isn't worth the effort to accomplish this as has been stated in other threads.
At the end of the day, I don't know if I've ever even used number literal types other than in a few toys to show you can; I find strings and enums to be more semantically meaningful for representing finite sets of things, so I use those literal types almost exclusively. With numbers, I almost always want to add, subtract, and multiply arbitrary values at which point you're going to widen any literal types you have anyways.
That being said, if Typescript is choosing to disallow NaN and Infinity types, then I think it should disallow anything that parses to +-Infinity as well as the literal word Infinity. 🙃
Reacted by Jonah Snider, Ricardo Fernández Serrata and Jimmy Zhening Luo
One of my favorite anti-tautologies in Javascript is that typeof(NaN) === 'number'; that which is in fact not a number is in fact number. Forget the usually silliness people point out like [] == false. You just don't get great existential conundrums in languages that make developers choose the signedness, size, and fixed or floatiness of their numbers.
IEEE floats actually aren’t that goofy (outside of signed zero and NaN which are IMO both abominations) once you realize it’s just scientific notation in base 2 instead of 10. You have a finite number of bits to represent the mantissa, so it’s only natural that things like repeating decimals and irrationals can’t be represented in that system. That’s also where the “floating” in floating point comes from - increasing the exponent moves the decimal point within a fixed-size mantissa, so you gain insane amounts of range at the expense of precision on the high-end. It’s kind of fascinating—to me anyway.
Even the NaN and signed zero serve valid purposes. I actually wrote a technical article on why we have NaN in our floating-point standard, if you are interested. The TL;DR is that if you think it's an abomination, you haven't seen the things it's replacing.
As for signed zeros, those are apparently used for contour integration in complex analysis. While I personally know what those words actually mean, I've never needed to actually implement a program that needs the distinction.
[...] those are apparently used for contour integration in complex analysis
Actually, -0 means "any negative Real whose order-of-magnitude is too low to be representable as a fixed-precision Float", which is very useful in the context of calculus, because of limits. Limits are the easiest part of calculus, and they are implicitly used outside of calculus as well. This is the reason why 1 / 0 == Infinity && 1 / -0 == -Infinity instead of NaN. See this video by Jan Misali for more info.
However, to be fair, Object.is(Math.sqrt(-0), -0) && Object.is((-0) ** +0.5, +0) is extremely weird and non-sensical. See related Reddit post
One of my favorite anti-tautologies in Javascript is that typeof(NaN) === 'number'; that which is in fact not a number is in fact number. Forget the usually silliness people point out like [] == false. You just don't get great existential conundrums in languages that make developers choose the signedness, size, and fixed or floatiness of their numbers.
Well, if you actually think about it, if it was an actual number, there would be neither reason nor sense to name it "Not a number", would there? The whole idea behind NaN is that it is a number that is not a number. :D
Inspired by this discussion and by others as well, I tried to implement a FiniteNumber type, and I think I found something that seems to work for all my use cases (maybe I'm wrong tho), here is how I did it, for anyone interested.
I didn't know about this implementation that you shared, and even if I did, it doesn't really add anything, the one thing that is in common between what you shared and what I did, is the Infinity hidden type, which both, that solution and mine got from the first comment of this discussion, so literally, even if I knew about that solution it wouldn't help me to achieve anything more than reading the first comment here.
Again I'm still learning, and I believe that reinventing the wheel is a good way to learn, and sharing the way that you implemented a solution to something, even if another implementation of the same solution already exists, isn't bad in of itself. So even if I reinvented the wheel in this case, I wouldn't mind it one bit, because I would've learned a lot while doing it.
Now the issue here, is that, if you just tried to read what I shared, you would've understood that my implementation is entirely different and adds more to the already existing one that you shared (that again I didn't know about), because as stated before that solution only creates a Finite type using the already shared infinity hidden type here, my implementation started an entirely different way, as it is based not on the Infinity hidden type, but on the fact that the values Infinity and NaN are not literal number types, and so it creates a LiteralNumber type that is the basis of my solution and works for both Infinity and NaN values (which the solution you shared doesn't), and I only added the Infinity hidden type to address one limitation of that. I won't go into details because I already explain this in my page. I also added this section comparing the two solutions if you are interested.
Anyway, I'm not claiming to be smart, or to have discovered something never seen before, I'm just sharing what I found in the hope that it may help others or just interest them. There are billions of humans out there, and for sure some of them did already discover the same thing, I just shared it here, because I saw some wanting the same thing, and I also searched a lot for something that answers all my needs and didn't find one, or maybe I'm just bad at searching, who knows !
I was confused until I realized you want to exclude NaN
if you just tried to read what I shared
I did read it before posting the link! But I was so focused on the implementation that I forgot it was meant to exclude NaN. It was an oversight of mine 😅
Inspired by this discussion and by others as well, I tried to implement a FiniteNumber type, and I think I found something that seems to work for all my use cases (maybe I'm wrong tho), here is how I did it, for anyone interested.
Activity
nmain commentedon Jun 4, 2019
Of course it doesn't quite work out, as neither
InfinityorInfinity as constwill satsifytype Inf. Pretty cool though!dragomirtitian commentedon Jun 4, 2019
Nicholai Main (@nmain) Fun trick. Github definitely needs a "please don't put that in your code reaction" 😊
jcalz commentedon Jun 4, 2019
🤔
AnyhowStep commentedon Jun 5, 2019
I don't care what the warnings are. I want those NaN and Infinity literals. I also want a "finite number" type.
I'm going to find some way to shoehorn that Infinity trick into my code and see if anything explodes
fatcerberus commentedon Jun 5, 2019
IEEE
INFhas some really useful properties which aren’t strictly mathematically correct (such asx / 0 = inforx % inf = x) that I’ve actually found it useful in practice from time to time.That said—not useful enough to inject that much evilness into my code. 😄
This one is kind of interesting, because technically by the meaning of
extends, the conditional type is correct anyway:-0is indeed substitutable for0(as they compare equal). Likewise for the ones with rounding errors.rickwebiii commentedon Jun 5, 2019
At the end of the day, floats are goofy and it might be fine to let the type system reflect that except in a few edge cases you really want to manage. There are an infinite number of decimal representations (memory granted) of every non-NaN number, and I don't think you can rightly ordain one as provenance. This means roundoff on types is just going to be a thing.
+- 0 is usually not a useful distinction. Most of the cases I can think about are pedantry. For example, Math.sign returns -0 if you pass -0, so you could type its return value as -1 | -0 | NaN | 0 | 1. Without the -0 type, you might be surprised that at runtime 1 / Math.sign(x) can return -Infinity while the return type led you to believe you had to only check NaN, Infinity, +-1. But this is also moot without a NaN literal type.
I don't know that much about type theory, but I think of the number type as being a very large finite union of one representation of every non-NaN floating point binary sequence (including distinct +0 and -0 and +Infinity and -Infinity) and a single NaN. NaN has many binary representations, but you can't tell them apart in Javascript. This is regardless of what the equality operator does. This looks like:
This is how I mentally reason about floating point numbers and having a type system that matches that would be convenient for reasoning about Typescript numbers. However, 1:1 matching the floating point number line to type literals my not be desirable in a type system for reasons I don't yet understand as I'm not a compiler expert and it probably isn't worth the effort to accomplish this as has been stated in other threads.
At the end of the day, I don't know if I've ever even used number literal types other than in a few toys to show you can; I find strings and enums to be more semantically meaningful for representing finite sets of things, so I use those literal types almost exclusively. With numbers, I almost always want to add, subtract, and multiply arbitrary values at which point you're going to widen any literal types you have anyways.
That being said, if Typescript is choosing to disallow NaN and Infinity types, then I think it should disallow anything that parses to +-Infinity as well as the literal word Infinity. 🙃
rickwebiii commentedon Jun 5, 2019
One of my favorite anti-tautologies in Javascript is that
typeof(NaN) === 'number'; that which is in fact not a number is in fact number. Forget the usually silliness people point out like[] == false. You just don't get great existential conundrums in languages that make developers choose the signedness, size, and fixed or floatiness of their numbers.fatcerberus commentedon Jun 5, 2019
IEEE floats actually aren’t that goofy (outside of signed zero and NaN which are IMO both abominations) once you realize it’s just scientific notation in base 2 instead of 10. You have a finite number of bits to represent the mantissa, so it’s only natural that things like repeating decimals and irrationals can’t be represented in that system. That’s also where the “floating” in floating point comes from - increasing the exponent moves the decimal point within a fixed-size mantissa, so you gain insane amounts of range at the expense of precision on the high-end. It’s kind of fascinating—to me anyway.
theverymodelofamodernmajorgeneral commentedon Sep 15, 2022
Even the NaN and signed zero serve valid purposes. I actually wrote a technical article on why we have NaN in our floating-point standard, if you are interested. The TL;DR is that if you think it's an abomination, you haven't seen the things it's replacing.
https://www.lucidchart.com/techblog/2022/03/04/if-its-not-a-number-what-is-it-demystifying-nan-for-the-working-programmer/
As for signed zeros, those are apparently used for contour integration in complex analysis. While I personally know what those words actually mean, I've never needed to actually implement a program that needs the distinction.
Rudxain commentedon Nov 10, 2022
Multi-reply
Quoting Bruce Pascoe (@fatcerberus)
Quoting theverymodelofamodernmajorgeneral
Actually,
-0means "any negative Real whose order-of-magnitude is too low to be representable as a fixed-precision Float", which is very useful in the context of calculus, because of limits. Limits are the easiest part of calculus, and they are implicitly used outside of calculus as well. This is the reason why1 / 0 == Infinity && 1 / -0 == -Infinityinstead ofNaN. See this video by Jan Misali for more info.However, to be fair,
Object.is(Math.sqrt(-0), -0) && Object.is((-0) ** +0.5, +0)is extremely weird and non-sensical. See related Reddit postraegen commentedon Feb 1, 2025
Well, if you actually think about it, if it was an actual number, there would be neither reason nor sense to name it "Not a number", would there? The whole idea behind NaN is that it is a number that is not a number. :D
amine-err commentedon Dec 25, 2025
Inspired by this discussion and by others as well, I tried to implement a
FiniteNumbertype, and I think I found something that seems to work for all my use cases (maybe I'm wrong tho), here is how I did it, for anyone interested.Rudxain commentedon Dec 26, 2025
Amine ER (@amine-err) It's already done; but since you're learning, I encourage you to continue writing your own solutions!
amine-err commentedon Dec 26, 2025
I didn't know about this implementation that you shared, and even if I did, it doesn't really add anything, the one thing that is in common between what you shared and what I did, is the
Infinityhidden type, which both, that solution and mine got from the first comment of this discussion, so literally, even if I knew about that solution it wouldn't help me to achieve anything more than reading the first comment here.Again I'm still learning, and I believe that reinventing the wheel is a good way to learn, and sharing the way that you implemented a solution to something, even if another implementation of the same solution already exists, isn't bad in of itself. So even if I reinvented the wheel in this case, I wouldn't mind it one bit, because I would've learned a lot while doing it.
Now the issue here, is that, if you just tried to read what I shared, you would've understood that my implementation is entirely different and adds more to the already existing one that you shared (that again I didn't know about), because as stated before that solution only creates a
Finitetype using the already sharedinfinityhidden type here, my implementation started an entirely different way, as it is based not on theInfinityhidden type, but on the fact that the valuesInfinityandNaNare not literal number types, and so it creates aLiteralNumbertype that is the basis of my solution and works for bothInfinityandNaNvalues (which the solution you shared doesn't), and I only added theInfinityhidden type to address one limitation of that. I won't go into details because I already explain this in my page. I also added this section comparing the two solutions if you are interested.Anyway, I'm not claiming to be smart, or to have discovered something never seen before, I'm just sharing what I found in the hope that it may help others or just interest them. There are billions of humans out there, and for sure some of them did already discover the same thing, I just shared it here, because I saw some wanting the same thing, and I also searched a lot for something that answers all my needs and didn't find one, or maybe I'm just bad at searching, who knows !
Rudxain commentedon Dec 26, 2025
I was confused until I realized you want to exclude
NaNI did read it before posting the link! But I was so focused on the implementation that I forgot it was meant to exclude
NaN. It was an oversight of mine 😅rickwebiii commentedon Dec 28, 2025
Using a bug as a feature, tsk tsk.
So this is how Windows 95 wound up with
ifProgramIsSimCityThenDontImmediatelyFreeMemory()check.