0

Sometimes I may need to limit the value range of a variable, and I expect to get the following types.

type Range<start, end, step extends number = 1> = ?

type a1 = Range<1000, 2000>; // 1000 | 1001 | 1002 | ... | 2000
type a2 = Range<1000, 2000, 2>; // 1000 | 1002 | ... | 2000

The above code allows me to have a Range of values between 1000 and 2000, although the values I want may be larger, such as 5000 and above. So Far I've only written a Range of 1000, i have no idea how to implement a Range that supports large numbers.

——————————————————————————————————————————

I accepted the help of netizens, and now I have written the Range function I want, you can check this link:link

The range supports up to tens of thousands of numbers, but too large a range of numbers may have performance problems, I think if the range of more than ten thousand to do further optimization can have better performance, but I have not yet supported.

Autumn
  • 161
  • 6
  • See if this answer helps: https://stackoverflow.com/a/39495173/79677 – Chris Barr Aug 14 '23 at 15:07
  • My answer to [this question](https://stackoverflow.com/q/76880509/2887218) mentions how this isn't really something intended to be supported by TypeScript, and all the issues involving programmatic types with recursion limits, union size limits, tuple length limits, etc. I've asked multiple times but I don't think I've really gotten an answer: *why* do you want to do this, given how it's not really supported? What concrete real-world use case does this address? It would help me answer if I knew what the underlying issue actually is. – jcalz Aug 14 '23 at 15:11
  • By 'large number' are you referring to bigint? – Jay Buckman Aug 14 '23 at 15:12
  • I can suggest [this](https://www.typescriptlang.org/play?ts=5.0.4#code/JYWwDg9gTgLgBAbzgSQM4HEoFMCGMtQAqAFjgHYA0cAygK4hwC+cAZlBAwOQwCeYWAWlQ4QtKDmCcA3ACgZvfnABK5AOZYAPDLhwAYuwZYAHvjIATVHDL0ARgQra4hCHGOmLV2-cdKsqWgA28G5Y5pbWIHZQANoAunAAvHDR+hyxDgB8iSgY2HgEJOQaqSBUzlkhYXAwULRYjgD8yQB0rb7+QWUQsdERUfGVHsBkLATKriahHn0EjjpNShPulkpzOnBNaJi4+ESkZBpKXRWTVTV1a+sbVlgAbrNXVwBcypdwL2R3D+sfX1COLxUZHUWkeNHoxQMVAArBkHGDnPDHtFWs12oEYFQSlQ6CBIRwqABGOHgvHYuAAJhJuPxpTgAGZqRDyQAWDKxNYZWTyPhYOAAQWyQJBAAYqCyRZKuXIAMYQMioeA4F6CpL0gCcmtkcoV8BsKuyEpF9NkQA) and it can be expanded, but I really wouldn't recommend :D – wonderflame Aug 14 '23 at 15:20
  • The *right* answer here is to contribute meaningfully to [ms/TS#54925](https://github.com/microsoft/TypeScript/issues/54925) to get such things actually supported (and note, "meaningfully" no longer means the verbal equivalent of an upvote... it's a Feature Update meaning there are concrete issues being discussed that need to be addressed, and anything like "I want this too" will be deleted). Any answer involving abusing template literals to do base-10 arithmetic should be presented with a huge bold warning about how it's for entertainment purposes only or something. – jcalz Aug 14 '23 at 15:34
  • @jcalz I explain why I raise this question from three angles – Autumn Aug 15 '23 at 03:59
  • @jcalz 1-0.First of all, thank you for your help.But I think it is not wrong to put forward reasonable suggestions. I have also written many code bases. When I design the library, I will not assume that I am smarter than the user and can think of all the scenarios of the user, so as to block some functions to avoid the user being stupid, and I will not block the functions that may be useful because of a potential problem. I have realized all the reasonable suggestions put forward by the user. – Autumn Aug 15 '23 at 04:01
  • @jcalz 1-1.Good suggestions are valuable to me. I will not say to my users that you should not give me reasonable suggestions, but you can provide me with specific codes. – Autumn Aug 15 '23 at 04:01
  • @jcalz 2-0.When I use many libraries, I feel deeply that many perfect ideas can't be achieved because the author deliberately blocks the functions, which makes me regret. I know why those functions are blocked, but I think the official reasons are not sufficient. The authors of the libraries can't imagine the advantages that those functions may bring. Of course, I don't mean that I think so, and the ts team should also think so. This is my statement of my personal thoughts. – Autumn Aug 15 '23 at 04:02
  • @jcalz 2-1 and then get back to the topic. For the function of Range, I just think it is an obviously useful function. There are some variables in the business that mean a Range of values, such as rgb color value 0-255, readers of a 500-page book may turn to page numbers between 1-500, etc. The users of ts make it work in unexpected places, and I can't think of all the scenarios. – Autumn Aug 15 '23 at 04:03
  • @jcalz 3.Finally explain what I'm doing? I'm not playing around with ts features, I'm writing a ts type toolset that I expect to use to support the type functionality of my other code base to make types smarter, Range is just something that I think might be useful, and like I said at the beginning, anything that might be useful should be implemented. I don't limit users by assuming I'm smarter than them, the problem is born out of my desire to find better implementations for the features I want. – Autumn Aug 15 '23 at 04:03
  • @jcalz Sorry, there are too many words, so I commented separately. – Autumn Aug 15 '23 at 04:04
  • @JayBuckman This means only larger numbers, not bigint, Such as: Range<10000, 20000> – Autumn Aug 15 '23 at 04:06
  • @wonderflame Thank you, my original direction is to recursively generate more numbers every time, so that the range of numbers can be as large as possible. Your code gives me a good example. – Autumn Aug 15 '23 at 06:14
  • @ChrisBarr Thank you, this writing method is currently similar to my own implementation method, but I think I now have the idea to implement a more powerful Range. – Autumn Aug 15 '23 at 06:17
  • I've read these comments and unfortunately this does not change my view here. Given that what you're attempting to do is at or past the edge of what TS can achieve today, efforts in this direction are better spent trying to get TS to actually support range types natively. If you had a particular set of use cases in mind then maybe I'd have some other ideas, but if it's really "I don't want to limit what users do" then ms/TS#54925 is the only responsible answer. – jcalz Aug 15 '23 at 12:54
  • @jcalz Thanks for your help, I think I can almost achieve the function I want, these are inspired by a lot of useful answers, I think I only need a little time to do it, thank you very much – Autumn Aug 15 '23 at 15:43
  • @wonderflame I think your code is helpful to my question, would you like to write an answer for me to adopt? – Autumn Aug 15 '23 at 15:44
  • sure, will do soon – wonderflame Aug 15 '23 at 15:45
  • One more time and then I'll disengage: Given that `Range<10000,20000>` is listed as an example in [a comment](//stackoverflow.com/q/76900052/2887218#comment135572254_76900052) about what "large number" support means, I still think there should be a **big caveat** that this is either [not going to work](https://tsplay.dev/WGXPvw), or cause your CPU to heat up whenever you edit your code, or both. – jcalz Aug 15 '23 at 15:49

1 Answers1

1

Since the limit of the tail recursion is 1000 there is no straight way to achieve this, however, the possible improvement would be to add not just one element per iteration but more. If we add two per iteration we can reach up to 2000 elements. This way it can be scaled even more, however, it's going to cause a really hard time for the compiler.

I will use some utility types from type-samurai package:

IsGreaterThan - Returns a boolean whether the first passed integer is greater than the second integer. Range: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER].

Sum - Accepts two integers and returns their sum. Range: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]

The strategy: The type accepts three parameters:

  • From - range start
  • To - range end
  • Result - an array of added numbers, defaults to [].

We going to check whether from is already greater than To in that case, using the indexed access we are going to get a union of all elements and store them in the new generic parameter by using infer keyword.

Then, by using distribution we are going to compare every member of the union with the To and remove them if they are greater than it.

If From is lower than To, we are going to add 5 to From and add From + 1,... From + 4 to the Result and call the Range recursively:

import { IsGreaterThan, Sum } from 'type-samurai';

type Range<
  From extends number,
  To extends number,
  Result extends number[] = [From],
> = IsGreaterThan<From, To> extends true
  ? [...Result, To][number] extends infer R extends number
    ? R extends R
      ? IsGreaterThan<R, To> extends true
        ? never
        : R
      : never
    : never
  : Range<
      Sum<From, 5>,
      To,
      [...Result, From, Sum<From, 1>, Sum<From, 2>, Sum<From, 3>, Sum<From, 4>]
    >;

Testing:

type A = Range<0, 4000>;

const a: A = 3999; // no error
const b: A = 4003; // error

playground

wonderflame
  • 6,759
  • 1
  • 1
  • 17