Created
July 12, 2022 06:24
-
-
Save vezaynk/310b14006af4531bb827e84111a3db15 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
interface StringDigitToNumber { | |
'0': 0, | |
'1': 1, | |
'2': 2, | |
'3': 3, | |
'4': 4, | |
'5': 5, | |
'6': 6, | |
'7': 7, | |
'8': 8, | |
'9': 9, | |
} | |
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0'; | |
type NumericStringAsDigitArray<T extends `${Digit}${string}`> = | |
T extends `${infer L extends Digit}${infer F extends string}` ? | |
F extends `${Digit}${string}` ? [StringDigitToNumber[L], ...NumericStringAsDigitArray<F>] : [StringDigitToNumber[L]] : never; | |
type Flatten<T extends any[]> = | |
T extends [infer First, ...infer Rest] | |
? First extends any[] | |
? Flatten<[...First, ...Rest]> | |
: [First, ...Flatten<Rest>] | |
: [] | |
type OneTwoThree = NumericStringAsDigitArray<"123">; | |
// ^? type OneTwoThree = [1, 2, 3] |
With that said, you basically want to avoid generating numbers for as long as you can, depending on what you're doing. The moment you replace strings with numbers, it becomes tricky.
interface StringDigitToNumber {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
}
interface PreviousNumber {
'0': '9',
'1': '0',
'2': '1',
'3': '2',
'4': '3',
'5': '4',
'6': '5',
'7': '6',
'8': '7',
'9': '8',
}
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0';
type NumericStringAsDigitArray<T extends `${Digit}${string}`> =
T extends `${infer L extends Digit}${infer F extends string}` ?
F extends `${Digit}${string}` ? [L, ...NumericStringAsDigitArray<F>] : [L] : never;
type OneTwoThree = NumericStringAsDigitArray<"123">;
// ^?
type MinusOneArray<T extends Digit[]> = CleanLeadingZero<
T extends [...infer R extends Digit[], infer L extends Digit] ?
L extends "0" ?
[...MinusOneArray<R>, PreviousNumber[L]]
: [...R, PreviousNumber[L]]
: never>;
type CleanLeadingZero<T extends Digit[]> = T extends ['0', ...infer R extends Digit[]] ? R : T;
type Test1 = MinusOneArray<["1", "2", "3"]>;
// ^? ["1","2","2"]
type Test2 = MinusOneArray<["1", "0", "0"]>;
// ^? ["9", "9"]
Going to bed.
Using others' solutions, we can join and convert the result to a number. A function MinusOne<>
generic is waiting:
interface StringDigitToNumber {
'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9,
}
interface PreviousNumber {
'0': '9',
'1': '0',
'2': '1',
'3': '2',
'4': '3',
'5': '4',
'6': '5',
'7': '6',
'8': '7',
'9': '8',
}
type Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' | '0';
type NumericStringAsDigitArray<T extends `${Digit}${string}`> =
T extends `${infer L extends Digit}${infer F extends string}` ?
F extends `${Digit}${string}` ? [L, ...NumericStringAsDigitArray<F>] : [L] : never;
type OneTwoThree = NumericStringAsDigitArray<"123">;
// ^?
type MinusOneArray<T extends Digit[]> = CleanLeadingZero<
T extends [...infer R extends Digit[], infer L extends Digit] ?
L extends "0" ?
[...MinusOneArray<R>, PreviousNumber[L]]
: [...R, PreviousNumber[L]]
: never>;
type CleanLeadingZero<T extends Digit[]> = T extends ['0', ...infer R extends Digit[]] ? R : T;
type Test1 = MinusOneArray<["1", "2", "3"]>;
// ^?
type Test2 = MinusOneArray<["1", "0", "0"]>;
// ^?
type Join<
T extends any[],
U extends string | number,
R extends string = ''
> =
T extends [infer F,...infer L]?
L['length'] extends 0?
`${R extends ''?'':`${R}${U}`}${F&string}`
:Join<L,U,`${R extends ''?'':`${R}${U}`}${F&string}`>
:R
type Test3 = Join<Test2, ''>;
type Mul10<I extends ReadonlyArray<any> = []> = [...I, ...I, ...I, ...I, ...I, ...I, ...I, ...I, ...I, ...I]
type Dec2Arr = {
'0': [],
'1': [1],
'2': [1, 1],
'3': [1, 1, 1],
'4': [1, 1, 1, 1],
'5': [1, 1, 1, 1, 1],
'6': [1, 1, 1, 1, 1, 1],
'7': [1, 1, 1, 1, 1, 1, 1],
'8': [1, 1, 1, 1, 1, 1, 1, 1],
'9': [1, 1, 1, 1, 1, 1, 1, 1, 1],
}
type DecChar = keyof Dec2Arr
type Str2Arr<S extends string, Remain extends ReadonlyArray<any> = []> =
S extends DecChar ? [...Mul10<Remain>, ...Dec2Arr[S]] :
S extends `${infer Leading}${infer Trailing}` ? (
Leading extends DecChar
? Str2Arr<Trailing, [...Mul10<Remain>, ...Dec2Arr[Leading]]> : []
) : []
type ToNumber<S extends string> = Str2Arr<S>['length']
type result = ToNumber<Join<MinusOneArray<NumericStringAsDigitArray<'10000'>>,''>>; // 9999
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Interestingly numbers have worse inference than strings. This forces us to use the
StringDigitToNumber[L]
bit. Try removing it and see what happens!Spoiler: It's
[Digit, Digit, Digit]
because numbers don't get inferred from string literals.