Skip to content

Allow Inifinity and -Infinity as number literal types #32277

Description

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.

Activity

  1. poseidonCore commented on Jul 7, 2019

    @poseidonCore

    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.

  2. AnyhowStep commented on Jul 17, 2019

    @AnyhowStep
    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

  3. shreyasminocha commented on Nov 26, 2019

    @shreyasminocha

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

  4. appsforartists commented on Apr 6, 2021

    @appsforartists

    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.

  5. GuiltyDolphin commented on Jun 9, 2021

    @GuiltyDolphin

    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.

  6. IanBellomy commented on Jul 10, 2021

    @IanBellomy

    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
  7. Rudxain commented on Nov 10, 2022

    @Rudxain
  8. MichalMarsalek commented on Jan 12, 2023

    @MichalMarsalek
    Contributor

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

  9. smikitky commented on Feb 1, 2024

    @smikitky
    Author

    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.

  10. olsonpm commented on Aug 10, 2024

    @olsonpm

    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

  11. camsteffen commented on Sep 25, 2024

    @camsteffen

    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
    
  12. scibioulian commented on Apr 10, 2025

    @scibioulian

    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';
    
  13. smikitky commented on Apr 13, 2025

    @smikitky
    Author

    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.

  14. petamoriken commented on Jun 29, 2025

    @petamoriken
    Contributor

    Stage 2 Iterator.range seems to require the Infinity type.

    type Infinity = number
    declare var Iterator: {
        new (): Iterator<never, void, void>
        prototype: Iterator<never, void, void>
        range(start: number, end: number, option: number | NumericRangeOptions<number>): IterableIterator<number>
        range(start: bigint, end: bigint | Infinity, option: bigint | NumericRangeOptions<bigint>): IterableIterator<bigint>
    }
    interface NumericRangeOptions<T extends bigint | number> {
        step?: T
        inclusive?: boolean
    }

    https://github.com/tc39/proposal-iterator.range/blob/main/global.d.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Awaiting More FeedbackThis means we'd like to hear from more people who would be helped by this featureSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions