Skip to content

Allow Inifinity and -Infinity as number literal types #32277

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
smikitky opened this issue Jul 6, 2019 · 13 comments
Open

Allow Inifinity and -Infinity as number literal types #32277

smikitky opened this issue Jul 6, 2019 · 13 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@smikitky
Copy link

smikitky commented Jul 6, 2019

Please accept Infinity and -Infinity as valid number literal types.

/** A range of BigInt that may be unbounded, e.g., (-∞, 5] */
class BigIntRange {
  public min: bigint | -Infinity = 0n; // <= Error!
  public max: bigint | Infinity = 1n; // <= Error!
  public isValid(): boolean {
    return this.min <= this.max;
  }
}

Currently, This causes a compile error, 'Infinity' refers to a value, but is being used as a type here. (2749).

I understand this was once rejected as part of #15135 and #15356, and the given reasons were: 1) difficult to implement, and 2) lacks a compelling use case.

However, while I understand why NaN as a literal type is tricky to implement, NaN and Infinity are not the same. And I have a clear use case for Infinity as a meaningful literal type, as shown above.

Infinity is not tricky to compare

Unlike notorious NaN or -0, Infinity works just like an ordinary numeric constant as far as equality is concerned. We don't need a special function like isNaN() or Object.is(). Ever since the ES5/IE6 era, there has been nothing counter-intuitive:

Infinity === Infinity // => true
-Infinity === -Infinity // => true
Number.POSITIVE_INFINITY === Infinity // => true
Object.is(-Infinity, Number.NEGATIVE_INFINITY) // => true
10 ** 500 === Infinity // => true (because 10**500 is above Number.MAX_VALUE)

typeof Infinity === 'number' // => true
typeof Number.NEGATIVE_INFINITY === 'number' // => true

// FWIW, comparison works just as expected, too
50 < Infinity // => true
-Infinity < 50; // => true
10n ** 500n < Infinity // => true
Number.MAX_VALUE < Infinity // => true
Infinity < Infinity // => false

Most importantly, Infinity === Infinity is true (while NaN === NaN is false). Unless I'm missing something, predictable equality is all that's required to safely use Infinity as a literal type, right? Even though the design note (#15356) says "there is more than one NaN, Infinity, etc", you can think of Infinity in JavaScript as "just a fixed number" which happens to be larger than Number.MAX_VALUE.

My use case

My library deals with unbounded (aka infinite) integer ranges, and I have been using Infinity and -Infinity without any issue to denote what they literally mean, infinity in the mathematical sense. I have instructed my users to use these interesting constants, too. Recently I started to extend my library to support bigint in addition to number, and ran into this problem.

You may ask "Why don't you just use string 'Infinity' or Symbol('unbounded')", but Infinity is a predefined and predictable constant, and it can be directly compared with any bigint (e.g., 10n ** 1000n < Infinity is true). See how simple the implementation of isValid can be in the first example.

PS: Looks like there is a hacky workaround (#31752), but I'd like to see the official support.

@poseidonCore
Copy link

poseidonCore commented Jul 7, 2019

Also, to state the obvious in favour of this, typeof Infinity = "number", not some odd object.

Quizzically, Infinity has some odd type conversions internally that may be at the heart of this:

TS Playground ts3.5.1

type numberNames = {0: "zero", 1: "one", Infinity: "inf"};
type numberTypes = keyof numberNames;

// These are fine:
var zero:numberTypes = 0;
var one:numberTypes = 1;

// This is an expected error:
var two:numberTypes = 2;
/* ERROR
var two: 0 | 1 | "Infinity"
Type '2' is not assignable to type '0 | 1 | "Infinity"'.
*/

// This is an unexpected error:
var inf:numberTypes = Infinity;
/* ERROR
var inf: 0 | 1 | "Infinity"
Type 'number' is not assignable to type '0 | 1 | "Infinity"'.
*/

Here, Infinity is treated as a string ... not a number, unlike the other number keys.

So, Infinity might be treated just like any other simple string key here, at least.

@RyanCavanaugh RyanCavanaugh added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Jul 8, 2019
@AnyhowStep
Copy link
Contributor

const Inf = 999e308;
type Infinity = 999e999999; //Just for fun, it's still the same as `Inf`

//We use `Inf` instead of `Infinity`
type numberNames = {0: "zero", 1: "one", [Inf]: "inf"};
type numberTypes = keyof numberNames;

// These are fine:
var zero:numberTypes = 0;
var one:numberTypes = 1;

// This is an expected error:
var two:numberTypes = 2;
/* ERROR
var two: 0 | 1 | "Infinity"
Type '2' is not assignable to type '0 | 1 | "Infinity"'.
*/

// OK!
var inf:numberTypes = Infinity as Infinity;

Playground

@shreyasminocha
Copy link

I have a very similar use case and was surprised that this doesn't work.

@appsforartists
Copy link

Also prevents you from making Infinity a mandatory key in a Record:

export type ImageSrcSet = Record<number, string> & { [Infinity]: string };

I'd expect that to resolve to a numeric dictionary where all keys are optional except for Infinity. Instead, it triggers TS1170: a computed property name must be a literal type.

@GuiltyDolphin
Copy link

I agree it makes sense to add Infinity and -Infinity - these could be treated as top/bottom elements (pity we don't have orderings in types). And as with the reference from #44408, some operations have special behaviour for Infinity, so it would be handy to be able to describe this behaviour in the types.

@IanBellomy
Copy link

IanBellomy commented Jul 10, 2021

Additional silly (?) use case:

Consider a roleplaying game where players roll a dice against an unconstrained difficulty number.

You'd like a type that includes the possible die results plus an 'auto-succeed' value for peculiar circumstances, e.g.

type D6 = 1|2|3|4|5|6|Infinity

@Rudxain

This comment has been minimized.

@MichalMarsalek
Copy link
Contributor

I also ran into this when implementing unbounded ranges, hoping that bigint | Infinity would be possible.

@smikitky
Copy link
Author

smikitky commented Feb 1, 2024

I have been waiting for this for over 4 years.

Unless I'm missing something, (-)Infinity is the only distinct primitive value that TypeScript cannot correctly treat in a literal type context. Aside from NaN (which is not "distinct" in the first place), all primitive values, including bigints and symbols, can be correctly handled as a literal type. I personally think this is more of a defect than a feature request.

Infinity is the only member of the number type that is guaranteed to be larger than any bigint, which is important in some practical scenarios. Infinity is not some "error value" that arises from some unexpected calculation result, but is a fully-fledged constant whose behavior is strictly and rationally defined in the world of numerical computation in JavaScript.

@olsonpm
Copy link

olsonpm commented Aug 10, 2024

My usecase for Infinity is a "flatten" utility accepting a depth of number including Infinity. It would be nice to overload for the case of depth: Infinity. Creating a separate function or signature for this case would be expanding the api only to appease typescript

@camsteffen
Copy link

As long as Infinity exists in JavaScript, I think people will find reasonable use cases for it from time to time, and TypeScript is limiting its utility for no good reason.

Today I wanted to write a function like

expiry(): Date | Infinity

@scibioulian
Copy link

I have code/comments like the following for years. Would love to finally be able to do it the "right" way.

// Preferably use -Infinity(NegativeInfinity) and Infinity(PositiveInfinity)
  // once available as types
  // (see https://github.com/microsoft/TypeScript/issues/32277)

  value: DateTime | 'INFINITY';

@smikitky
Copy link
Author

smikitky commented Apr 13, 2025

The Infinity constant is not a JavaScript-only invention. It is a well-defined value from the IEEE 754 floating-point standard, which JavaScript (like many other languages) follows for its number type.

The infinities of the extended real number line can be represented in IEEE floating-point datatypes, just like ordinary floating-point values like 1, 1.5, etc. They are not error values in any way, though they are often (depends on the rounding) used as replacement values when there is an overflow. Upon a divide-by-zero exception, a positive or negative infinity is returned as an exact result. An infinity can also be introduced as a numeral (like C's "INFINITY" macro, or "∞" if the programming language allows that syntax).

At the bit level, Infinity is represented by a sign bit of 0, an exponent of all ones (0x7FF), and a mantissa of all zeros:

  • Binary: 0 11111111111 0000000000000000000000000000000000000000000000000000
  • Hex: 0x7FF0000000000000

Similarly, -Infinity is simply 0xFFF0000000000000.

This means that Infinity is not some exotic concept; it's a precise, stable value interoperable across major programming languages like C, Java, Python, and Go, via something like Float64Array. There is no reason not to support this technically well-grounded value as a literal type in TypeScript. In my opinion, not supporting this at the type level would mean math in TypeScript is not fully standard-compliant yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.