-
-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Open
Labels
2595answerShare answers/solutions to a questionShare answers/solutions to a questionenin Englishin English
Description
2257- MinusOne
Considering all the amazing things you can do with TypeScript, this feels like one that should be easy, but it's anything but. You need to create temporary Array accumulators and other messy business. Worth a look on "wow factor" alone.
🎥 Video Explanation
🔢 Code
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
type A1 = MinusOne<1>
type B1 = 0;
type C1 = Expect<Equal<A1, B1>>;
type A2 = MinusOne<55>
type B2 = 54;
type C2 = Expect<Equal<A2, B2>>;
type A3 = MinusOne<3>
type B3 = 2;
type C3 = Expect<Equal<A3, B3>>;
type A4 = MinusOne<100>
type B4 = 99;
type C4 = Expect<Equal<A4, B4>>;
type A5 = MinusOne<1101>
type B5 = 1100;
type C5 = Expect<Equal<A5, B5>>;
type A6 = MinusOne<0>;
type B6 = -1;
type C6 = Expect<Equal<A6, B6>>;
// type A7 = MinusOne<9007199254740992>;
// type B7 = 9007199254740991;
// type C7 = Expect<Equal<A7, B7>>;
// ============= Your Code Here =============
type Tuple<
N extends number,
T extends unknown[] = []
> =
0 extends 1
? never
: T["length"] extends N
? T
: Tuple<N, [...T, unknown]>;
type MinusOne<T extends number> =
Tuple<T> extends [unknown, ...infer R]
? R["length"]
: -1;
// ============== Alternatives ==============
// credit to @Drincann
type Reverse<S extends string> =
S extends `${infer Head}${infer Tail}`
? `${Reverse<Tail>}${Head}`
: S
;
type Times<T extends unknown[]> = {
'0': [],
'1': [...T],
'2': [...T, ...T,],
'3': [...T, ...T, ...T,],
'4': [...T, ...T, ...T, ...T,]
'5': [...T, ...T, ...T, ...T, ...T,],
'6': [...T, ...T, ...T, ...T, ...T, ...T,],
'7': [...T, ...T, ...T, ...T, ...T, ...T, ...T,],
'8': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T,],
'9': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T,],
'10': [...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T, ...T,]
}
type _List<
N extends string,
Base extends any[] = [any],
Acc extends any[] = []
> =
N extends `${infer Head extends keyof Times<any>}${infer Tail}`
? (
Tail extends ''
/* to avoid generating tuples with a length of 10000 */
? [...Acc, ...Times<Base>[Head]]
: _List<
Tail,
Times<Base>['10'],
[...Acc, ...Times<Base>[Head]]
>
)
: never;
type List<N extends number> = _List<Reverse<`${N}`>>;
type MinusOne<
T extends number,
Arr extends any[] = List<T>
> =
Arr extends [...infer Head, unknown]
? Head['length']
: -1
;
type MinusOne<
T extends number,
Tuple extends unknown[] = []
> =
T extends 0
? -1
: [unknown, unknown, ...Tuple]['length'] extends T
? [unknown, ...Tuple]['length']
: [unknown, ...Tuple]['length'] extends T
? Tuple['length']
: MinusOne<T, [unknown, unknown, ...Tuple]>
// credit to @santosmarco-caribou
// THIS ONE PASSES C7
type MinusMap = {
'0': -1
'1': 0
'2': 1
'3': 2
'4': 3
'5': 4
'6': 5
'7': 6
'8': 7
'9': 8
}
type _GetHeadAndLast<
T,
Acc extends string = ''
> =
T extends `${infer Head}${infer Tail}`
? Tail extends ''
? [Acc, Head]
: _GetHeadAndLast<Tail, `${Acc}${Head}`>
: never;
type GetHead<T> = _GetHeadAndLast<T>[0]
type GetLast<T> = _GetHeadAndLast<T>[1]
type ToNumber<T> =
T extends `${infer U extends number}`
? U
: never;
type MinusOne<T extends number> =
`${T}` extends infer _T extends string
? _T extends keyof MinusMap
? MinusMap[_T]
: ToNumber<
GetLast<_T> extends '0'
? `${GetHead<_T> extends '1'
? ''
: MinusOne<GetHead<_T>>}9`
: `${GetHead<_T>}${MinusMap[GetLast<_T>]}`
>
: never;
// credit to @gaac510
// THIS ONE PASSES C7
type OneLess = {
readonly "9": "8";
readonly "8": "7";
readonly "7": "6";
readonly "6": "5";
readonly "5": "4";
readonly "4": "3";
readonly "3": "2";
readonly "2": "1";
readonly "1": "0";
};
type MinusOne<
T extends number | string,
Digits extends string[] = [],
Processed extends string[] = [],
Rejoined extends string = ""
> =
`${T}` extends `${infer First}${infer Remainder}`
// STAGE 1: split the digits
? MinusOne<Remainder, [...Digits, First]>
// STAGE 2: process each digits
: Digits extends [
...infer OtherDigits extends string[],
infer RightMostDigit
]
? RightMostDigit extends keyof OneLess
// When not 0
? MinusOne<
"",
[],
[
...OtherDigits,
OneLess[RightMostDigit],
...Processed
]
>
// When 0
: MinusOne<
"",
OtherDigits,
["9", ...Processed]
>
// STAGE 3: Join digits into a string.
: Processed extends [
infer First extends string,
...infer Remainder extends string[]
]
? MinusOne<"", [], Remainder, `${Rejoined}${First}`>
// STAGE 4: Handle edge cases and return result
: Rejoined extends "9"
// When it was 0 turned into 9
? -1
: Rejoined extends `0${infer Answer extends number}`
// When the left most digit is 0
? Answer
: Rejoined extends `${infer Answer extends number}`
// Convert to number
? Answer
: never;
// ================== NOPE ==================
// hits recursion error:
//
// Type instantiation is excessively deep
// and possibly infinite.ts(2589)
type Pop<T extends unknown[]> =
T extends [...infer Head, unknown]
? Head
: never
;
type MinusOne<
T extends number,
Acc extends unknown[] = []
> =
Acc['length'] extends T
? Pop<Acc>['length']
: MinusOne<T, [...Acc, 0]>
;
// fails C6, credit to @elias-prine
type ParseInt<T extends string> =
T extends `${infer Digit extends number}`
? Digit
: never;
type ReverseString<S extends string> =
S extends `${infer First}${infer Rest}`
? `${ReverseString<Rest>}${First}`
: '';
type RemoveLeadingZeros<S extends string> =
S extends '0'
? S
: S extends `${'0'}${infer R}`
? RemoveLeadingZeros<R>
: S;
type InternalMinusOne<S extends string> =
S extends `${infer Digit extends number}${infer Rest}`
? Digit extends 0
? `9${InternalMinusOne<Rest>}`
: `${[9, 0, 1, 2, 3, 4, 5, 6, 7, 8][Digit]}${Rest}`
: never;
type MinusOne<T extends number> =
ParseInt<
RemoveLeadingZeros<
ReverseString<
InternalMinusOne<
ReverseString<`${T}`>
>
>
>
>;
// fails C6, credit to @matcher-ice
type DigitToArray = {
"0": [],
"1": [unknown],
"2": [unknown, unknown],
"3": [unknown, unknown, unknown],
"4": [unknown, unknown, unknown, unknown],
"5": [unknown, unknown, unknown, unknown, unknown],
"6": [unknown, unknown, unknown, unknown, unknown, unknown],
"7": [unknown, unknown, unknown, unknown, unknown, unknown, unknown],
"8": [unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown],
"9": [unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown, unknown]
};
type CreateArrayByLength<
N extends string,
R extends unknown[] = []
> =
N extends `${infer First}${infer Rest}`
? First extends keyof DigitToArray
? CreateArrayByLength<
Rest,
[
...R, // 0
...R, // 1
...R, // 2
...R, // 3
...R, // 4
...R, // 5
...R, // 6
...R, // 7
...R, // 8
...R, // 9
...DigitToArray[First]]
>
: never
: R
;
type MinusOne<T extends number> =
CreateArrayByLength<`${T}`> extends
[unknown, ...infer Rest]
? Rest["length"]
: never
;➕ More Solutions
For more video solutions to other challenges: see the umbrella list! #21338
goni-ssi and pzj01
Metadata
Metadata
Assignees
Labels
2595answerShare answers/solutions to a questionShare answers/solutions to a questionenin Englishin English