type GetLengthedTuple<N extends number,Result extends any[]= []> = Result['length'] extends N ? Result: GetLengthedTuple<N,[...Result,Result['length']]>
// type t = GetLengthedTuple<54>
type MinusOne<N extends number, T extends any[] = GetLengthedTuple<N>> = T extends [any,...infer Rest]?Rest['length']:never
type Zero = MinusOne<1> // 0
type FiftyFour = MinusOne<55> // 54
Solution by Gravity2333 #37069
type GetNumber<T extends number, K extends any[] = []> = K['length'] extends T ? K : GetNumber<T, [...K, any]>
type MinusOne<T extends number> = T extends 0 ? 0 : GetNumber<T> extends [infer _F, ...infer R] ? R['length'] : never
Solution by 359Steve #37004
目前在挑战中等难度,这是最难的一集,硬控我一小时。。。 基本思路就是: 把number字面量转字符串,用SubtractOne映射实现一位的减法,然后用MinusOneByStr 处理,如果返回的是9,那么继续递归,最后再处理首位的0转回number字面量
type SubtractOne<T extends string> =
T extends '1' ? '0' :
T extends '2' ? '1' :
T extends '3' ? '2' :
T extends '4' ? '3' :
T extends '5' ? '4' :
T extends '6' ? '5' :
T extends '7' ? '6' :
T extends '8' ? '7' :
T extends '9' ? '8' :
T extends '0' ? '9' : never
type GetStringLast<S extends string> = S extends `${infer F}${infer Rest}` ? Rest extends '' ? F : GetStringLast<Rest> :never
type StringToNumber<S extends string> = S extends `${infer N extends number}` ? N : never;
type FilteZero<S extends string> = S extends `0${infer R}` ? R extends '' ? S : R : S
type MinusOneByStr<S extends string> = S extends `${infer Rest}${GetStringLast<S>}`
? S extends `${Rest}${infer E}`
? SubtractOne<E> extends '9' ? `${MinusOneByStr<Rest>}${SubtractOne<E>}` : `${Rest}${SubtractOne<E>}`
: never
: never
type MinusOne<T extends number> = StringToNumber<FilteZero<MinusOneByStr<`${T}`>>>
Solution by deerro #36977
type MinusOne<N extends number, T extends any[] = []> = T["length"] extends N
? T extends [infer L, ...infer Rest]
? Rest["length"]
: 0
: MinusOne<N, Push<T, N>>;
Solution by tingtingxiao0706 #36435
type DecMap = {9:8; 8:7; 7:6; 6:5; 5:4; 4:3; 3:2; 2:1; 1:0; 0:9;};
type DigitsToTuple<T extends string | number> =
`${T}` extends `${infer A extends keyof DecMap}${infer B}`
? [A, ...DigitsToTuple<B>]
: [];
type NumberTupleToString<T extends number[]> = T extends []
? ""
: T extends [infer F extends number, ...infer R extends number[]]
? `${F}${NumberTupleToString<R>}`
: never;
type StringToNumber<S extends string> = S extends `${infer N extends number}` ? N : never;
type RemoveZero<T extends number[]> = T extends [0, ...infer R extends number[]] ? R extends [] ? T : R : T;
type MinusNumberTuple<T extends number[], R extends number[] = []> = T extends [
...infer A extends number[],
infer B extends keyof DecMap
]
? B extends 0
? MinusNumberTuple<A, [DecMap[B], ...R]>
: [...A, DecMap[B], ...R]
: never;
type MinusOne<T extends number> = StringToNumber<NumberTupleToString<RemoveZero<MinusNumberTuple<DigitsToTuple<T>>>>>;
Solution by vowdemon #36326
// your answers
type MinusOne<T extends number, Tuple extends unknown[] = []> = T extends 0
? -1
: [unknown, ...Tuple]["length"] extends T
? Tuple["length"]
: MinusOne<T, [unknown, ...Tuple]>;
Solution by Sathiyapramod #35537
interface MinusMap {
"9": "8";
"8": "7";
"7": "6";
"6": "5";
"5": "4";
"4": "3";
"3": "2";
"2": "1";
"1": "0";
}
type getLastCharOfString<S extends string> = S extends `${infer F}${infer R}`
? R extends ""
? F
: getLastCharOfString<R>
: never;
type ConvertString2Number<T extends string> = T extends `0${infer N}`
? N extends '' ? 0 : ConvertString2Number<N>
: T extends `${infer M extends number}`
? M
: never;
type MinusOneButString<T extends number, F = `${T}`> = F extends `${infer R extends number}0`
? `${MinusOneButString<R>}9`
: F extends `${infer X}${infer R}`
? R extends ''
? X extends keyof MinusMap ? MinusMap[X] : never
: F extends `${infer B}${getLastCharOfString<F>}`
? `${B}${MinusMap[getLastCharOfString<F>]}`
: never
: never;
// For number small than 0
// its same logic with the code above. Only need to get the oppisite direction of MinusMap
type MinusOne<T extends number> = T extends 0
? -1
: ConvertString2Number<MinusOneButString<T>>;
Solution by Yaphet2015 #35176
type Map = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8]
type ReverseString<T extends string> = T extends `${infer First}${infer Rest}` ? `${ReverseString<Rest>}${First}` : T
type Decrease<T extends string> = T extends `${infer First extends number}${infer Rest}` ? `${Map[First]}${First extends 0 ? Decrease<Rest> : Rest}` : T
type ParseInt<T extends string> = T extends `${0}${infer Rest}` ? ParseInt<`${Rest}`> : (T extends `${infer N extends number}` ? N : 0)
type MinusOne<T extends number> = T extends 0 ? -1 : ParseInt<ReverseString<Decrease<ReverseString<`${T}`>>>>
Solution by 2083335157 #34894
type ParseInt<T extends string> = T extends `0${infer S}` ? ParseInt<S> : T extends `${infer R extends number}` ? R : 0
type Reverse<T extends string, S extends string = ''> = `${T}` extends `${infer L}${infer R}`? Reverse<R, `${L}${S}`> : S
type MinusOneWithout0<T extends number> = `${T}` extends `${infer R extends number}0`
? `${MinusOneWithout0<R>}9`
: Reverse<`${T}`> extends `${infer L extends number}${infer R}`
? Reverse<`${[9, 0, 1, 2, 3, 4, 5, 6, 7, 8][L]}${R}`>
: never
type MinusOne<T extends number> = T extends 0
? -1
: ParseInt<MinusOneWithout0<T>>
Solution by hrc1457 #34851
// your answers
type Reverse<T extends string, R extends string = ''> = `${T}` extends `${infer First}${infer Rest}` ? Reverse<Rest, `${First}${R}`> : R;
type StringToNumber<T extends string> = T extends `${infer N extends number}`? N : never;
type TrimLeadingZero<T extends string> = T extends `0${infer Rest}` ? TrimLeadingZero<Rest> : T extends '' ? '0' : T;
type MinusOne<T extends number, R extends string = ''> = Reverse<`${T}`> extends `${infer First}${infer Rest}`
? First extends '0'
? Rest extends '' ? -1 : MinusOne<StringToNumber<Reverse<Rest>>, `${R}9`>
: StringToNumber<TrimLeadingZero<Reverse<`${R}${MinusMap<First>}${Rest}`>>>
: never;
Solution by AndreGeng #34757
type Pop<T extends any[]> = T extends [...infer head, any] ? head : never;
type MinusOne<T extends number, A extends any[] = []> = A['length'] extends T
? Pop<A>['length']
: MinusOne<T, [...A, 0]>
Solution by devshinthant #34673
type MapMinusOne = {
"0": "9";
"1": "0";
"2": "1";
"3": "2";
"4": "3";
"5": "4";
"6": "5";
"7": "6";
"8": "7";
"9": "8";
};
// split number to tuple of digits
// eg.
// "12" -> ["1", "2"]
// "5" -> ["5"]
type SplitToDigit<A extends string> = A extends `${infer L}${infer R}`
? R extends ""
? [L]
: [L, ...SplitToDigit<R>]
: [];
// minus one from ones place
// eg.
// ["1", "2"] -> ["1", "1"]
// ["1", "0"] -> ["0", "9"]
// ["0"] -> ["-1"]
type MinusOneTuple<T extends string[]> = T extends [...infer L extends string[], infer R extends keyof MapMinusOne]
? R extends "0"
? L extends []
? ["-1"]
: [...MinusOneTuple<L>, "9"]
: [...L, MapMinusOne[R]]
: [];
// eg.
// ["0", "9", "9"] -> ["9", "9"]
// ["0"] -> ["0"]
// ["1", "2"] -> ["1", "2"]
type RemoveHeadZero<T extends string[]> = T extends [infer L, ...infer R]
? L extends "0"
? R extends []
? ["0"]
: R
: T
: [];
// eg.
// ["1", "2"] => "12"
type TupleToNumber<T extends string[]> = T extends [infer L extends string, ...infer R extends string[]]
? `${L}${TupleToNumber<R>}`
: "";
type MinusOne<T extends number> = TupleToNumber<
RemoveHeadZero<MinusOneTuple<SplitToDigit<`${T}`>>>
> extends `${infer U extends number}`
? U
: never;
Solution by yukicountry #34410
type NumberMap = ['9', '0', '1', '2', '3', '4' ,'5', '6', '7', '8']
// number to char array
type NumToCharArr<T extends number | string> = `${T}` extends `${infer F}${infer R}` ? [F, ...NumToCharArr<R>] : []
// reverse array
type ReverseArr<T extends any[]> = T extends [infer F, ...infer R] ? [...ReverseArr<R>, F] : T
// char -1
type CharSubOne<T extends string> = T extends keyof NumberMap ? NumberMap[T] : never
// char array item -1
type ArrSubOne<T extends any[]> = T extends [infer F extends string, ...infer R]
? F extends '0'
? ['9', ...ArrSubOne<R>]
: [CharSubOne<F>, ...R]
: [];
// char array to string
type CharArrToStr<T extends any[]> = T extends [infer F extends string, ...infer R] ? `${F}${CharArrToStr<R>}` : ''
// string to number
type StrToNum<T extends string> = T extends `0${infer R}`
? StrToNum<R>
: T extends `${infer R extends number}`
? R
: 0;
type MinusOne<T extends number> = T extends 0 ? -1 : StrToNum<CharArrToStr<ReverseArr<ArrSubOne<ReverseArr<NumToCharArr<T>>>>>>
Solution by MAXLZ1 #34242
type ToNumber<T> = T extends `0${infer N extends number}` ? N : T extends `${infer N extends number}` ? N : T
type DigitToArr<T extends number, R extends unknown[] = []> = R['length'] extends ToNumber<T> ? R : DigitToArr<T, [...R, 0]>
type NumberToArr<T extends number | string, Arr extends unknown[][] = []> = `${T}` extends `${infer A extends number}${infer Rest}` ? NumberToArr<Rest, [...Arr, DigitToArr<A>]> : Arr
type MinusArr<R extends unknown[]> = R extends [...infer A, infer B] ? B extends [...infer R, infer _] ? [...A, R] : [...MinusArr<A>, DigitToArr<9>] : []
type ArrToString<Arr extends unknown[][], R = ''> = Arr extends [infer A extends unknown[], ...infer rest extends unknown[][]] ? `${A['length']}${ArrToString<rest>}` : R
type MinusOne<T extends number> = T extends 0 ? -1 : ToNumber<ArrToString<MinusArr<NumberToArr<T>>>>
Solution by ouzexi #34045
/* _____________ 你的代码 _____________ */
type ReverseString<S extends string> =
S extends `${infer First}${infer Rest}`
? `${ReverseString<Rest>}${First}`
: "";
type DigitStrMinusOne<T extends string> =
"09876543210" extends `${string}${T}${infer Right}${string}`
? Right
: never;
type RevNumStrMinusOne<T extends string> =
T extends `${infer First}${infer Rest}`
? DigitStrMinusOne<First> extends `${infer Result}`
? Rest extends ""
? `${Result}`
: Result extends "9"
? `${Result}${RevNumStrMinusOne<Rest>}`
: `${Result}${Rest}`
:never
: "";
type DigitStrPlusOne<T extends string> =
"01234567890" extends `${string}${T}${infer Right}${string}`
? Right
: never;
type RevNumStrPlusOne<T extends string> =
T extends `${infer First}${infer Rest}`
? DigitStrPlusOne<First> extends `${infer Result}`
? Rest extends ""
? Result extends "0"
? `${Result}1`
: `${Result}`
: Result extends "0"
? `${Result}${RevNumStrPlusOne<Rest>}`
: `${Result}${Rest}`
:never
: "";
type RemovePaddingZero<S extends string> =
S extends `0${infer Rest}`
? Rest extends ""
? "0"
: `${RemovePaddingZero<Rest>}`
: S;
type MinusOne<T extends number> =
T extends 0
? -1
: `${T}` extends `-${infer Digits}`
? `-${ReverseString<RevNumStrPlusOne<ReverseString<Digits>>>}` extends `${infer Result extends number}`
? Result
: never
: `${RemovePaddingZero<ReverseString<RevNumStrMinusOne<ReverseString<`${T}`>>>>}` extends `${infer Result extends number}`
? Result
: never;
Solution by CarrickC #34001
// 你的答案
// 将数字转为对应长度的数组 如将2 转换为 [null, null]
type DigitToArray<T extends number, R extends any[] = []> = R['length'] extends T ? R : DigitToArray<T, [...R, null]>
// 将数字(字符串)的每一位都转为对应长度的数组,如将 '23' 转换为 [[null, null], [null, null, null]]
type WholeDigitStringToArray<T extends string, Res extends any[][] = []> = T extends `${infer A extends number}${infer R}` ?
WholeDigitStringToArray<R, [...Res, DigitToArray<A>]>
: Res
// 将数字的每一位都转为对应长度的数组,如将 23 转换为 [[null, null], [null, null, null]]
type WholeDigitToArray<T extends number> = WholeDigitStringToArray<`${T}`>
// 翻转数组(要从个位 - 十位 - 百位 依次去减一)
type MyReverse<T extends any[], Res extends any[] = []> = T extends [...infer A, infer R] ? MyReverse<A, [...Res, R]> : Res
// 逐位减一,返回当前位减一的结果,及是否产生了借位
type MinusBitByBit<T extends any[]> = T extends [infer _, ...infer R] ? [R['length'], false] : [9, true]
// 将数组中的每一项展示为对应数组的长度 如将[[null, null], [null, null, null]] 转为 [2, 3]
type ArrayEveryToNumber<T extends any[][], Res extends any[] = []> = T extends [infer A extends any[], ...infer R extends any[][]] ? ArrayEveryToNumber<R, [...Res, A['length']]> : Res
// 数组整体减一操作
type WholeArrayMinusOne<T extends any[], Res extends any[] = []> = T extends [infer A extends any[], ...infer R extends any[]] ?
(MinusBitByBit<A> extends [infer X, infer Y] ?
Y extends true ? WholeArrayMinusOne<R, [...Res, X]> : [...Res, X, ...ArrayEveryToNumber<R>]
: Res)
: Res
// 将数组转为字符串
type ArrayToString<T extends any[], Res extends string = ''> = T extends [infer A extends number, ...infer R] ? ArrayToString<R, `${Res}${A}`> : Res
// 将字符串转为数字
type StringToNumber<T extends string> =T extends 0 ? 0 : T extends `0${infer A extends number}` ? A : T extends `${infer F extends number}` ? F : T
type MinusOne<T extends number> =T extends 0 ? -1 : StringToNumber<ArrayToString<MyReverse<WholeArrayMinusOne<MyReverse<WholeDigitToArray<T>>>>>>
Solution by heyuelan #33698
type NumberList = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8];
type ReverseStr<T extends string | number> = (T extends string ? T : ${T}
) extends ${infer L}${infer R}
? ${ReverseStr<R>}${L}
: T;
type RemoveFirstZero${infer L}${infer R}
? L extends '0'
? R extends ''
? ${T}
: ${RemoveFirstZero<R>}
: T
: never;
type Parse2Int${infer N extends number}
? N
: never
: never;
type LoopStr${infer L extends number}${infer R}
? L extends 0
? ${NumberList[L]}${LoopStr<R>}
: ${NumberList[L]}${R}
: '';
type MinusOne
Solution by huangxinrui #33253
// your answers
type ReverseString<S extends string> =
S extends "" ?
S
:
S extends `${infer c}${infer remaining}` ?
`${ReverseString<remaining>}${c}`
:never
;
type TrimLeadingZeros<T extends string> =
T extends '0' ?
T
:
T extends `0${infer remaining}` ?
TrimLeadingZeros<remaining>
:
T
;
type NumberToString<T extends number> = `${T}`;
type StringToNumber<T extends string> = TrimLeadingZeros<T> extends `${infer N extends number}`? N : never;
//only use for postive number
type MinusOneInternal<T extends number ,borrow extends boolean = false> =
ReverseString<NumberToString<T>> extends `${infer First extends number}${infer Remaining extends string}` ?
DigitMinusOne<First> extends [ infer F extends number,infer Borrow extends boolean] ?
Borrow extends true ?
StringToNumber<`${MinusOneInternal<StringToNumber<ReverseString<Remaining>>,true>}${F}`>
:
StringToNumber<`${ReverseString<Remaining>}${F}`>
:
never
:never;
//only use for postive number
type AddOneInteral<T extends number,carry extends boolean = false> =
ReverseString<NumberToString<T>> extends `${infer First extends number}${infer Remaining extends string}` ?
DigitAddOne<First> extends [ infer F extends number,infer Carry extends boolean] ?
Carry extends true ?
Remaining extends "" ?
StringToNumber<`1${F}`>
:
StringToNumber<`${AddOneInteral<StringToNumber<ReverseString<Remaining>>,true>}${F}`>
:
StringToNumber<`${ReverseString<Remaining>}${F}`>
:
never
:NumberToString<T> extends "" ?
carry extends true ?
1
:
0
:
never;
;
type DigitAddOne<n extends number > =
n extends 9 ?
[0,true]
:
[[1,2,3,4,5,6,7,8,9][n],false] ;
;
type DigitMinusOne<n extends number > =
n extends 0 ?
[9,true]
:
[[9,0,1,2,3,4,5,6,7,8][n],false] ;
type MinusOne<S extends number> =
S extends 0 ?
-1
:
NumberToString<S> extends `-${infer positiveNumber extends number}` ?
`-${AddOneInteral<positiveNumber>}` extends `${infer num extends number}` ?
num
:
never
:
NumberToString<S> extends `${infer positiveNumber extends number}` ?
MinusOneInternal<S,false>
:
never
;
Solution by sciencefunq #32929
type step1ParseInt<T extends string> = T extends `${infer Digit extends number}`?Digit : never;
type step2ReverseString<S extends string> = S extends `${infer First}${infer Rest}` ? `${step2ReverseString<Rest>}${First}` : ""
type step3RemoveLeadingZeros<S extends string> = S extends '0' ? S : S extends `${0}${infer Rest}` ? step3RemoveLeadingZeros<Rest>:S
type step4InternalMinusOne<S extends string> =
S extends `${infer Digit extends number}${infer Rest}`?
Digit extends 0?
`9${step4InternalMinusOne<Rest>}`:
`${[9, 0, 1, 2, 3, 4, 5, 6, 7, 8][Digit]}${Rest}`
:never
type MinusOne<T extends number> = T extends 0? -1 :step1ParseInt<step3RemoveLeadingZeros<step2ReverseString<step4InternalMinusOne<step2ReverseString<`${T}`>>>>>
Solution by bananana0118 #32906
type StrToArr<T extends string, Arr extends string[] = []> =
`${T}` extends `${infer A}${infer B}`
? StrToArr<B, [...Arr, A]>
: Arr
type ParseInt<T extends string> =
T extends `${infer A extends number}` ? A : never;
type InitArr<T extends number, Arr extends unknown[] = []> =
Arr["length"] extends T ? Arr : InitArr<T, [...Arr, unknown]>
type SubTra<T extends number, K extends number> =
InitArr<T> extends [...args: InitArr<K>, ...infer R] ? R['length'] : never;
type ArrToNum<T extends string[], Str extends string = ""> =
T extends [infer A extends string, ...infer B extends string[]]
? ArrToNum<B, `${Str}${A}`>
: ParseInt<Str>
type GreaterThan<T extends number, K extends number> =
SubTra<T, K> extends never ? false : true;
type MinusOne<T extends number> =
T extends 0
? -1
: GreaterThan<StrToArr<`${T}`>['length'],4> extends false
? SubTra<T,1>
: StrToArr<`${T}`> extends [...infer A,infer B extends string]
? ParseInt<B> extends 0
? never
: [...A,`${SubTra<ParseInt<B>,1>}`] extends [...infer A extends string[]]?ArrToNum<A>:never
:never
有点取巧,判断小于4位数就直接使用SubTra减一即可,最好是在倒数第三行处处理一下0的情况,但是比较麻烦加上猪脑过载,没做出来QAQ
Solution by I-am-a-king-of-vue #32147
// 你的答案
// 由于有大数,递归会导致深度爆炸, 可以转成字符转进行运算
// -1 对照表
type reduceList = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8]
// 将字符串转为数字
type parseInt<T extends string> = T extends `${infer Digit extends number}` ? Digit : never;
// 翻转字符串
type reverseString<T extends string> = T extends `${infer F}${infer R}` ? `${reverseString<R>}${F}` : '';
// 去掉字符串前面所有的 0
type splitZero<T extends string> = T extends `${infer F}${infer L}` ? F extends '0' ? splitZero<L> : T : '0';
// 递归处理字符串
/**
* 思路:
* 1.每次取出最后一位, U 保存 -1 后的数字
* 2.通过判断 U 是不是等于 '', 来判断当前位需不需要 -1
*/
type recursiveString<T extends string, U extends string = '', Flag extends string = '0'> = T extends `${infer F}${infer L}`
? /** 当字符串可以拆开的时候,需要判断 U*/
Flag extends '0' // Flag 如果是 '0', 说明当前字符不需要 -1
? `${reverseString<T>}${U}`
: // Flag 如果是 '1', 说明当前字符需要 -1
// 这里需要判断当前字符是否是 '0'
F extends '0'
? recursiveString<L, `${reduceList[parseInt<F>]}${U}`, '1'> // 0 下一位需要 -1
: recursiveString<L, `${reduceList[parseInt<F>]}${U}`, '0'> // 非 0 下一位不需要 -1
: /** 当字符串拆不开的时候, 说明上面的遍历到头了 */
U extends '9' ? '-1' : splitZero<U>
type MinusOne<T extends number> = parseInt<recursiveString<reverseString<`${T}`>, '', '1'>>;
Solution by cutimgboy #31845
// Process...
Solution by jbalancer #31561
type ToNumber<S extends string> = S extends `${infer X extends number}` ? X : never;
type DigitMinusOne<
T extends '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
> = {
'9': '8',
'8': '7',
'7': '6',
'6': '5',
'5': '4',
'4': '3',
'3': '2',
'2': '1',
'1': '0'
}[T];
type LastChar<T extends string> =
T extends `${infer Head}${infer Tail}`
? Tail extends ''
? Head
: LastChar<Tail>
: never
;
type MinusOneInternal<T extends string> =
T extends '1'
? ''
: T extends `${infer U}0`
? ToNumber<`${MinusOneInternal<U>}9`>
: T extends `${infer U}${LastChar<T>}`
? ToNumber<`${U}${DigitMinusOne<LastChar<T>>}`>
: never
;
type MinusOne<T extends number> =
T extends 0
? -1
: T extends 1
? 0
: MinusOneInternal<`${T}`>
;
Solution by sdrjs #31513
// your answers
type mp = ['9', '0', '1', '2', '3', '4', '5', '6', '7', '8'];
type ParseInt<S> = S extends `${infer N extends number}` ? N : never;
type ReverseStr<S> = S extends `${infer F}${infer R}` ? `${ReverseStr<R>}${F}` : S;
// check S not 0
type _MinusOne<S extends string> = S extends `${infer F}${infer R}` ? F extends '0' ? `9${_MinusOne<R>}` : ParseInt<F> extends number ? `${mp[ParseInt<F>]}${R}` : never : never;
type _TrimLeftZero<S> = S extends `0${infer R}` ? _TrimLeftZero<R> : S;
type TrimLeftZero<S> = S extends `0` ? `0` : _TrimLeftZero<S>;
type __MinusOne<S extends number> = ParseInt<TrimLeftZero<ReverseStr<_MinusOne<ReverseStr<`${S}`>>>>>;
type MinusOne<S extends number> = S extends 0 ? -1 : __MinusOne<S>;
Solution by chenqy-yh #31079
type Digits = {
'0': '9'
'1': '0'
'2': '1'
'3': '2'
'4': '3'
'5': '4'
'6': '5'
'7': '6'
'8': '7'
'9': '8'
};
type StringToNumber<T extends string> = T extends `${infer A extends number}` ? A : never;
type Reverse<T extends string> = T extends `${infer H}${infer T}` ? `${Reverse<T>}${H}` : T;
type _MinusOne<T extends string> =
T extends `${infer Head extends keyof Digits}${infer Tail}`
? Head extends '0'
? `${_MinusOne<Tail>}${Digits[Head]}`
: `${Reverse<Tail>}${Digits[Head]}`
: never;
type RemoveLeadingZero<S extends string> = S extends `${infer H extends '0'}${infer T}` ? T extends '' ? S : RemoveLeadingZero<T> : S;
type MinusOne<T extends number> = T extends 0 ? -1 : StringToNumber<RemoveLeadingZero<_MinusOne<Reverse<`${T}`>>>>;
Solution by korkota #31060
type MyTuple<Num extends number, Arr extends readonly unknown[] = []> =
Arr['length'] extends Num
? Arr
: MyTuple<Num, [never, ...Arr]>
type MinusOne<T extends number> = MyTuple<T> extends [never, ...infer R] ? R['length'] : -1
Solution by HeeYeonKim98 #30838
This code creates excessive stack depth 💦
type SliceOne<Array extends unknown[]> =
Array extends [infer First, ...infer Rest]
? Rest
: [];
type Push<Array extends unknown[]> = [1, ...Array];
type GetNumber<GoalNumber extends number, TargetArray extends unknown[]> =
TargetArray['length'] extends GoalNumber
? TargetArray
: GetNumber<GoalNumber, Push<TargetArray>>;
type MinusOne<T extends number> =
// T extends [infer First, ...infer Rest extends unknown[]]['length']
T extends (infer Arr extends unknown[])['length']
? SliceOne<GetNumber<T, []>>['length']
: never;
Solution by KNHui #30832
type MakeNumberToArray<T extends number, U extends unknown[] = []> = U['length'] extends T
? U
: MakeNumberToArray<T, [...U, 0]>;
type MinusOne<T extends number> = MakeNumberToArray<T> extends [infer R, ...infer U]
? R extends undefined
? 0
: U['length']
: never;
Solution by leejaehyup #30783
type ConstructTuple<Num extends number, Arr extends readonly never[] = []> =
Arr['length'] extends Num
? Arr
: ConstructTuple<Num, [never, ...Arr]>
type MinusOne<T extends number> = ConstructTuple<T> extends [never, ...infer Rest] ? Rest['length'] : -1
Solution by GodAraden #30759
type Digit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type PrevDigit = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8];
type NextDigit = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
/** Reverses the string */
type Rev<T extends string> = T extends `${infer L}${infer R}` ? `${Rev<R>}${L}` : "";
/** Decreases positive number `T` by one */
type Decr<T extends number> = Rev<RevStrMinus<Rev<`${T}`>>> extends `${infer N extends number}` ? N : 0;
type RevStrMinus<T extends string> = T extends `${infer D extends Digit}${infer Rest}`
? Rest extends ""
? D extends 1 ? "" : `${PrevDigit[D]}`
: D extends 0 ? `9${RevStrMinus<Rest>}` : `${PrevDigit[D]}${Rest}`
: never;
/** Increases positive number `T` by one */
type Incr<T extends number> = Rev<RevStrPlus<Rev<`${T}`>>> extends `${infer N extends number}` ? N : 0;
type RevStrPlus<T extends string> = T extends `${infer D extends Digit}${infer Rest}`
? D extends 9 ? `0${Rest extends "" ? "1" : RevStrPlus<Rest>}` : `${NextDigit[D]}${Rest}`
: never;
type MinusOne<T extends number> = T extends 0 ? -1 : `${T}` extends `-${infer P extends number}` ? `-${Incr<P>}` : Decr<T>;
There are plenty well explained solutions there, like #21627
To be concise:
0
is corner case, it always become -1
-
increase the absolute value by one and return it with sign -
Solution by orl0 #30746