Skip to content

Instantly share code, notes, and snippets.

@vezaynk
Created July 12, 2022 19:11
Show Gist options
  • Save vezaynk/60cf9bee0d9a1df2dccc2023027294c7 to your computer and use it in GitHub Desktop.
Save vezaynk/60cf9bee0d9a1df2dccc2023027294c7 to your computer and use it in GitHub Desktop.
MinusOne.ts
// Lookup table to seed the counting array
// Skips to length of 10, 100, 1000 instead of counting
// Allows going up to 1998 instead of 999!
// With some work, this technique can be pushed further for higher numbers
interface InitialLengths {
'2': [0, 0, 0, 0, 0,
0, 0, 0, 0, 0],
'3': [...InitialLengths[2], ...InitialLengths[2], ...InitialLengths[2], ...InitialLengths[2], ...InitialLengths[2],
...InitialLengths[2],...InitialLengths[2], ...InitialLengths[2], ...InitialLengths[2], ...InitialLengths[2]],
'4': [...InitialLengths[3], ...InitialLengths[3], ...InitialLengths[3], ...InitialLengths[3], ...InitialLengths[3],
...InitialLengths[3], ...InitialLengths[3], ...InitialLengths[3], ...InitialLengths[3], ...InitialLengths[3]],
}
// Accepts an array T, returns an array without the first element
type PopArray<T extends any[]> = T extends [infer First, ...infer Rest] ? [...Rest] : [];
// Convert string to array of characters
type StringToArray<S extends string> = S extends `${infer A}${infer B}`
? [A, ...StringToArray<B>]
: []
// Returns the number of digits in a number
type LengthOfNumber<T extends number> = StringToArray<`${T}`>['length'];
// Accepts a number T, and returns the number T-minus-one by increasing the
// size of the array by 1 until it T == U['length'],
// followed by applying PopArray<U>
type MinusOne<T extends number, U extends any[] = []> =
// Is T == U['length']
T extends U['length'] ?
// If yes, then remove the first element and return the length
PopArray<U>['length'] :
// (This is an optimization)
// Otherwise, check the length of the number. Do they match?
LengthOfNumber<T> extends LengthOfNumber<U['length']> ?
// If yes, increment the size of the array by one and recurse
MinusOne<T, [T, ...U]> :
// Otherwise, initialize the counting array to the closest of 10 / 100 / 1000
`${LengthOfNumber<T>}` extends infer L extends keyof InitialLengths ?
MinusOne<T, InitialLengths[L]> :
// Fail if higher than 9999 (It fails for other reasons anyways)
never;
type Zero = MinusOne<1>;
// ^?
type A = MinusOne<2>;
// ^?
type B = MinusOne<20>;
// ^?
type C = MinusOne<100>;
// ^?
type D = MinusOne<502>;
// ^?
type E = MinusOne<999>;
// ^?
type F = MinusOne<1004>;
// ^?
type G = MinusOne<1026>;
// ^?
type H = MinusOne<1006>;
// ^?
type I = MinusOne<1998>; // Works up to 1998!
// ^?
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment