02257-medium-minusone

Back

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 = T extends ${infer L}${infer R} ? L extends '0' ? R extends '' ? ${T} : ${RemoveFirstZero<R>} : T : never; type Parse2Int = T extends any ? RemoveFirstZero extends ${infer N extends number} ? N : never : never; type LoopStr = T extends ${infer L extends number}${infer R} ? L extends 0 ? ${NumberList[L]}${LoopStr<R>} : ${NumberList[L]}${R} : ''; type MinusOne = T extends 0 ? -1 : Parse2Int<ReverseStr<LoopStr<ReverseStr>>>;

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 RusJstudent #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:

  1. 0 is corner case, it always become -1
  2. If starts with - increase the absolute value by one and return it with sign -
  3. Else decrease the number
  4. To handle huge numbers we should handle each digit one by one
  5. Digits must be processed from left to right, so we need to reverse the string

Solution by orl0 #30746

// your answers

type MinusMap = {
  '0': '9',
  '1': '0',
  '2': '1',
  '3': '2',
  '4': '3',
  '5': '4',
  '6': '5',
  '7': '6',
  '8': '7',
  '9': '8',
}


type ReverseString<T extends string, R extends string = ''> = T extends `${infer F}${infer Rest}` ? ReverseString<Rest, `${F}${R}`> : R

type String2Number<T> = T extends `${infer T extends number}` ? T : never
type Number2String<T extends number> = `${T}`


/**
 * 1. 将数值反转, 如 100 => 001
 * 2. 01 -1 => 90 => 清空最后的0
 * 3. 001 -1 => 990 => 清空最后的0
 * 4. 0[2-9] => 9[1-8]
 * 5. 00[2-9] => 99[1-8]
 * 6. [1-9] => [0-8]
 */

type MinusReverse<T extends string, ClearLastZero = false> =
  T extends `${infer F}${infer Rest}`
    ? F extends '0'
      ? `9${MinusReverse<Rest, true>}`
      : F extends '1'
        ? ClearLastZero extends true 
          ? ''
          : `0${Rest}`
      : `${F extends keyof MinusMap ? MinusMap[F] : never}${Rest}`
   : never 

type MinusOne<T extends number> = T extends 0 ? -1 : String2Number<ReverseString<MinusReverse<ReverseString<Number2String<T>>>>>


Solution by enochjs #30475

type ToNumber<S extends string> =
    S extends `0${infer N}` ? N extends '' ? 0 : ToNumber<N> :
    S extends `${infer N extends number}` ? N :
    never

type LastDigit<S extends number> =
    `${S}` extends `${string}${infer T}` ?
        T extends '' ? S : LastDigit<ToNumber<T>> :
        never

type MinusOne<T extends number, L extends number = LastDigit<T>> =
    T extends L ?
        [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8][T] :
    `${T}` extends `${infer N extends number}${L}` ?
        ToNumber<L extends 0 ? `${MinusOne<N>}9` : `${N}${MinusOne<L, L>}`> :
    never

Explanation

Separating using extends `${infer A}${infer B}`​ splits into a single character A and a variable-length string B. As we need to separate the last digit, we have to use recursion.

A recursion using extends `${infer N1 extends number}${infer N2 extends number}`​ does not work when the number contains a zero digit: when N1 precedes a zero digit, N2 , because of its leading zero(s), would not be parsed as a number literal type but just as type number. Thus, we need a recursion that parses string literals.

ToNumber strips off leading zeros using recursion and returns either 0 if nothing is left or parses the remaining number literal using ​`${infer N extends number}`​.

LastDigit strips off the first digit with extends `${string}${infer T}`​ and removes leading zeros from T with ToNumber before passing the resulting number literal type to the recursive call of itself.

Initializing a second argument L of MinusOne with the LastDigit of its first argument T as a default value comes handy in the definition of MinusOne.

If T extends L, T is a single digit itself, and it can be used to index an array with the results for the values 09.

Otherwise, we can infer the number part preceding the last digit with extends `${infer N extends number}${L}`​. As L is already known, the first ${…} has variable length and infers the complete number preceding the last digit without the need of recursion.

If L extends 0, we subtract 1 from N and append 9 in ​`${MinusOne<N>}9`​. If L is in the range 19, we keep N and subtract 1 from L in ​`${N}${MinusOne<L, L>}`​ (we pass L twice to override the default for the second parameter, because we have already computed its value). As we got the result as string literal type due to the concatenation, we finally need to use ToNumber to get the result as a number literal type.

Solution by sbr61 #30099

// 你的答案
type NumberToString<T extends number> = `${T}`
type StringToNumber<T extends string> = T extends `${infer F extends number}` ? F : never
type StringToArray<T extends string> = T extends `${infer F}${infer Rest}` ? [F, ...StringToArray<Rest>] : []
type MinusNum = {
  '0': '9'
  '1': '0'
  '2': '1'
  '3': '2'
  '4': '3'
  '5': '4'
  '6': '5'
  '7': '6'
  '8': '7'
  '9': '8'
}
type AddNum = {
  '0': '1'
  '1': '2'
  '2': '3'
  '3': '4'
  '4': '5'
  '5': '6'
  '6': '7'
  '7': '8'
  '8': '9'
  '9': '0'
}
type PosAndNeg<T extends string[]> = T extends [infer F, ...any] ? F extends '-' ? ArrayAdd<T> : ArraySub<T> : never

type ArraySub<T extends string[], U extends string[] = []> =
  T extends [...infer Rest extends string[], infer L] ?
  L extends keyof MinusNum ?
  L extends '0' ? ArraySub<Rest, [MinusNum[L], ...U]> : [...Rest, MinusNum[L], ...U]
  : never
  : never

type ArrayAdd<T extends string[], U extends string[] = []> =
  T extends [...infer Rest extends string[], infer L] ?
  L extends keyof AddNum ?
  L extends '9' ? ArrayAdd<Rest, [AddNum[L], ...U]> : [...Rest, AddNum[L], ...U]
  : U[0] extends '0' ? [L, '1', ...U] : [L, ...U]
  : 1

type ArrayToString<T extends string[]> =
  T extends [infer F extends string, ...infer Rest extends string[]] ? `${F}${ArrayToString<Rest>}` : ''
type Remove0<T extends string> = T extends `0${infer Rest}` ? Rest extends '' ? T : Remove0<Rest> : T
type MinusOne<T extends number> = T extends 0 ? -1 : StringToNumber<Remove0<ArrayToString<PosAndNeg<StringToArray<NumberToString<T>>>>>>

Solution by YE840926098 #30082

type Numbers = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9;
type N_MinusOne = [9, 0, 1, 2, 3, 4, 5, 6, 7, 8];
type N_PlusOne = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0];

type ParseNumber<T extends string> = T extends `0${infer N extends number}` ? N : T extends `${infer R extends number}` ?  R : never;
type ParseNegativeNumber<T extends string> = T extends `${infer N extends number}` ? N : never;

type MinusOne<T extends number> = T extends 0 ?  -1 :
                                    `${T}` extends `-${infer Neg extends number}` ? 
                                    // negative numbers
                                    ParseNegativeNumber<`${Neg}` extends `${infer R extends number}${Numbers}` ? 
                                        `${Neg}` extends `${R}${infer N extends number}` ? (N extends 9 ? `-${MinusOne<R>}${N_PlusOne[N]}` : `-${R}${N_PlusOne[N]}`) : `-${N_PlusOne[T]}`:
                                        `-${N_PlusOne[Neg]}`> :
                                    // positive numbers
                                    `${T}` extends `${infer R extends number}${Numbers}` ? 
                                        `${T}` extends `${R}${infer N extends number}` ? ParseNumber<(N extends 0 ? `${MinusOne<R>}${N_MinusOne[N]}` : `${R}${N_MinusOne[N]}`)> : N_MinusOne[T]:
                                      N_MinusOne[T];

Solution by rezof #29516

相对别人的索引法更笨一些的方法,先写出正数负数的计算规则,也就是这两个map,正数需要减去1,负数需要加上1

计算方式就是将数字转为字符串,再将字符串反转,如1002反转为2001,通过infer取到第一位,丢进map里转一下就行,稍微难一点的就是1000转为0001这种情况,取到第一位0,变为9以后,后续的数字也需要递归判断,结果为9990,需要额外去掉最后一位0

type PositiveMaps = {
  '0': '9'
  '1': '0'
  '2': '1'
  '3': '2'
  '4': '3'
  '5': '4'
  '6': '5'
  '7': '6'
  '8': '7'
  '9': '8'
}

type NegativeMaps = {
  '0': '1'
  '1': '2'
  '2': '3'
  '3': '4'
  '4': '5'
  '5': '6'
  '6': '7'
  '7': '8'
  '8': '9'
  '9': '0'
}

type PickMap<T extends number> = ToString<T> extends '0'
  ? NegativeMaps
  : ToString<T> extends `-${infer _}`
  ? NegativeMaps
  : PositiveMaps

type ToString<T extends number> = `${T}`
type Reverse<T extends string> = T extends `${infer R}${infer T}` ? `${Reverse<T>}${R}` : T
type ToNumber<T extends string> = T extends `${infer R extends number}` ? R : T
type RemoveFirstZero<T extends string> = T extends `0${infer R}` ? (R extends '' ? T : R) : T
type AddSymbol<S extends string, T extends number> = ToString<T> extends `0` ? `-${S}` : S
type MinusOneString<
  T extends string,
  Map extends PositiveMaps | NegativeMaps,
> = T extends `${infer Last}${infer Rest}`
  ? Last extends keyof PositiveMaps
    ? Last extends '0'
      ? `${Map[Last]}${MinusOneString<Rest, Map>}`
      : `${Map[Last]}${Rest}`
    : never
  : T

type MinusOne<T extends number> = ToNumber<
  AddSymbol<RemoveFirstZero<Reverse<MinusOneString<Reverse<ToString<T>>, PickMap<T>>>>, T>
>

Solution by zhangyu1818 #28854

// A version for both positive number and negative number.
type ParseInt<T extends string> = T extends `${infer Digit extends number}`
  ? Digit
  : T;

type ReverseStr<T extends string> = T extends `${infer F}${infer R}`
  ? `${ReverseStr<R>}${F}`
  : T;

type RemoveLeadingZero<T extends string> = T extends `0${infer R}`
  ? R extends ""
    ? "0"
    : `${RemoveLeadingZero<R>}`
  : T;

type InnerMinusOne<T extends string> =
  T extends `${infer F extends number}${infer R}`
    ? F extends 0
      ? `9${InnerMinusOne<R>}`
      : `${[9, 0, 1, 2, 3, 4, 5, 6, 7, 8][F]}${R}`
    : T;

type InnerPlusOne<T extends string> = T extends "9"
  ? "01"
  : T extends `${infer F extends number}${infer R}`
  ? F extends 9
    ? `0${InnerPlusOne<R>}`
    : `${[1, 2, 3, 4, 5, 6, 7, 8, 9][F]}${R}`
  : T;

type MinusOne<T extends number> = T extends 0
  ? -1
  : `${T}` extends `-${infer Num}`
  ? ParseInt<`-${RemoveLeadingZero<
      ReverseStr<InnerPlusOne<ReverseStr<`${Num}`>>>
    >}`>
  : ParseInt<RemoveLeadingZero<ReverseStr<InnerMinusOne<ReverseStr<`${T}`>>>>>;

Solution by DoubleWoodLin #28688

Answer works on small number. type MinusOne<T extends number, Arr extends unknown[] = []> = T extends 0 ? -1 : Arr["length"] extends T ? Arr extends [infer First, ...infer Rest] ? [...Rest]["length"] : never : MinusOne<T, [...Arr, number]>;

Solution by DoubleWoodLin #28687

type MinusMap = {
  "1": "0";
  "2": "1";
  "3": "2";
  "4": "3";
  "5": "4";
  "6": "5";
  "7": "6";
  "8": "7";
  "9": "8";
};

type StrToArray<
  T extends string,
  A extends string[] = []
> = T extends `${infer F}${infer R}` ? StrToArray<R, [...A, F]> : A;

type Minus<T extends unknown[]> = T extends [...infer P, infer L]
  ? L extends keyof MinusMap
    ? [...P, MinusMap[L]]
    : [...Minus<P>, "9"]
  : never;

type ArrayToStr<T extends unknown[], S extends string = ""> = T extends [
  infer F extends string,
  ...infer R
]
  ? ArrayToStr<R, `${S}${F}`>
  : S;

type RemoveFrontZero<T> = T extends `0${infer R}` ? R : T;
type ParseInt<T extends string> = T extends "0"
  ? 0
  : RemoveFrontZero<T> extends `${infer N extends number}`
  ? N
  : never;

type MinusOne<T extends number> = T extends 0
  ? -1
  : ParseInt<ArrayToStr<Minus<StrToArray<`${T}`>>>>;

Solution by DoGukKim #28375

// 思路:通过个位与个位,十位与十位,百位与百位...上的数进位相加/退位相减,得到一个二维数组,把这个数组通过取长度得到一维数组再连接成字符串,最后再转成数字得到结果
type StrOrNum = string | number;
type StrToNum<S> = S extends `${infer N extends number}` ? N : never;
type Shift<T extends any[]> = T extends [infer _, ...infer R] ? R : [];
// 将数字N分解成数组
type NumToDigitArr<N extends StrOrNum, M extends number[] = []> =
  `${N}` extends `${infer F extends number}${infer R}` ? [...M, F, ...NumToDigitArr<R, M>] : [];
// 将数字N转成N长度的数组
type NumToLengthArr<N extends number, B extends 1[] = []> = B['length'] extends N ? B : NumToLengthArr<N, [...B, 1]>;
type ArrToStr<A> = A extends [infer F extends StrOrNum, ...infer R] ? `${F}${ArrToStr<R>}` : '';
// 去除数字前面的0,比如‘-054’去0得‘-54’再转成number类型
type ShiftZero<S> = S extends `0${infer R}` ? R extends '' ? '0' : R : S;
// 取数值的正负符号,没有就为''
type GetNumSymbol<N extends StrOrNum> = `${N}` extends `${infer F}${infer _}` ? F extends '-' | '+' ? F : '' : '';
// 去掉符号取数值的数字位
type ParseIntArr<N extends StrOrNum> = `${N}` extends `${'+' | '-'}${infer R}` ? NumToDigitArr<R> : NumToDigitArr<N>;
// 0-9 的加1和减1操作,转成数组
type BaseAddOne<N extends number> = NumToDigitArr<([...NumToLengthArr<N>, 1]['length'] & StrOrNum)>;
type BaseReduceOne<N extends number> = NumToLengthArr<N> extends [infer _, ...infer R] ? NumToDigitArr<R['length']> : ['-', 9];
type MinusOne<T extends StrOrNum, A extends StrOrNum[] = ParseIntArr<T>, B extends StrOrNum[] = [GetNumSymbol<T> extends '-' ? '+' : '-', 1]> =
  T extends '0' | 0 ? -1   // 不便于在下面做判断,直接在这里做个特例
  : (B extends [infer Symbol extends '-' | '+', infer _, ...infer Rest extends StrOrNum[]] ?
    (A extends [...infer R extends StrOrNum[], infer L extends number] ?
      (GetNumSymbol<T> extends '-' ?
        MinusOne<T, R, [...(BaseAddOne<L>['length'] extends 2 ? [Symbol] : []), ...BaseAddOne<L>, ...Rest]>
        : (MinusOne<T, R, [...(BaseReduceOne<L>['length'] extends 2 ? [Symbol] : []), ...BaseReduceOne<L>, ...Rest]>))
      : StrToNum<`${GetNumSymbol<T> extends '-' ? '-' : ''}${ArrToStr<Shift<B>>}`>)
    : StrToNum<`${GetNumSymbol<T> extends '-' ? '-' : ''}${ShiftZero<ArrToStr<[...A, ...B]>>}`>)

Solution by smileboyi #27311

type MinusOneNums = {
  '1':0, '2':1, '3':2, '4':3, '5':4, '6':5, '7':6, '8':7, '9':8, '0':9
}

type Push<T extends any[], U> = T extends [infer F, ...infer O] ? [F, ...O,  U] : [U];

type ArrayToString<T extends any[]> = T extends [infer F, ...infer O] ? 
  F extends string ? 
  `${F}${ArrayToString<O>}` : '' : '';

type ParseInt<T extends string> = T extends `${infer F extends number}${infer O}` ?
  F extends 0 ? 
  O extends `${infer X extends number}` ? X : F :
  T extends `${infer Y extends number}` ? Y : never : never;


type ExceptLast<T extends string, V extends any[] = []> = T extends `${infer F}${infer O}` ? 
  ExceptLast<O, Push<V, F>> :
  V extends [...infer O, infer L] ? ArrayToString<O> : never


type MinusOne<T extends number, V extends string = ExceptLast<`${T}`>> = T extends 0 ? -1 : `${T}` extends `${V}${infer L}` ?
  L extends '0' ? ParseInt<`${MinusOne<ParseInt<V>>}${MinusOneNums[L]}`> : 
  L extends keyof MinusOneNums ? ParseInt<`${V}${MinusOneNums[L]}`> : never : never;

Solution by 8471919 #27098

Based on #13507, extended to handle 0 as well as negative numbers.

type RemoveLeadingZeros<T extends string> = T extends `0${infer V}`
	? V extends ''
		? '0'
		: RemoveLeadingZeros<V>
	: T;
type ParseInt<T extends string> =
	RemoveLeadingZeros<T> extends `${infer U extends number}` ? U : never;
type ReverseString<T extends string> = T extends `${infer L}${infer R}`
	? `${ReverseString<R>}${L}`
	: '';

type PositiveMinusOne<T extends string> =
	T extends `${infer D extends number}${infer R}`
		? D extends 0
			? `9${PositiveMinusOne<R>}`
			: `${[9, 0, 1, 2, 3, 4, 5, 6, 7, 8][D]}${R}`
		: never;

type NegativeMinusOne<T extends string> =
	T extends `${infer D extends number}${infer R}`
		? D extends 9
			? `0${NegativeMinusOne<R>}`
			: `${[1, 2, 3, 4, 5, 6, 7, 8, 9][D]}${R}`
		: never;

type MinusOne<T extends number> = T extends 0
	? -1
	: `${T}` extends `-${infer U extends number}`
	? ParseInt<`-${ReverseString<NegativeMinusOne<ReverseString<`${U}`>>>}`>
	: ParseInt<ReverseString<PositiveMinusOne<ReverseString<`${T}`>>>>;

Solution by SuperKXT #27022

// your answers
type NumberLiteral = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type MinusOneMap = {
  "0": 9
  "1": 0
  "2": 1
  "3": 2
  "4": 3
  "5": 4
  "6": 5
  "7": 6
  "8": 7
  "9": 8
}
type PlusOneMap = {
  "0": 1
  "1": 2
  "2": 3
  "3": 4
  "4": 5
  "5": 6
  "6": 7
  "7": 8
  "8": 9
  "9": 0
}

type NumberToString<T extends number> = `${T}`

type RemoveStartWithZeros<S extends string> = S extends "0"
  ? S
  : S extends `${0}${infer Rest}`
    ? `${RemoveStartWithZeros<Rest>}`
    : S

type ReverseString<S extends string> = S extends `${infer First}${infer Rest}`
  ? `${ReverseString<Rest>}${First}`
  : ""

type Initial<S extends string> = ReverseString<S> extends `${infer _}${infer Rest}`
  ? `${ReverseString<Rest>}`
  : S

type MinusOneForString<S extends string, T extends string = Initial<S>> = S extends `${T}${infer Last extends NumberLiteral}`
  ? Last extends '0'
    ? `${MinusOneForString<T>}${MinusOneMap[Last]}`
    : `${T}${MinusOneMap[Last]}`
  : never

type PlusOneForString<S extends string, T extends string = Initial<S>> = S extends `${T}${infer Last extends NumberLiteral}`
  ? Last extends '9'
    ? T extends '9'
      ? `${1}${PlusOneForString<T>}${PlusOneMap[Last]}`
      : `${PlusOneForString<T>}${PlusOneMap[Last]}`
    : `${T}${PlusOneMap[Last]}`
  : S

type GetSignSymbol<T extends string> = T extends `${infer _F extends '-'}${infer _L extends number}`
  ? '-'
  : '+'

type ParseInt<T extends string> =
  RemoveStartWithZeros<T> extends `${infer Digit extends number}`
    ? Digit
    : never

type MinusOne<T extends number, S = GetSignSymbol<`${T}`>> = T extends 0
  ? -1
  : S extends '+'
    ? ParseInt<MinusOneForString<NumberToString<T>>>
    : ParseInt<PlusOneForString<NumberToString<T>>>

Solution by daiki-skm #26588

// type MinusOne<T extends number, U extends number[] = [] > = U['length'] extends T
//   ? U extends [infer _, ...infer R] ? R['length'] : never
//   : MinusOne<T, [...U, T]>
type NumberLiteral = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
type MinusOneMap = {
  "0": 9
  "1": 0
  "2": 1
  "3": 2
  "4": 3
  "5": 4
  "6": 5
  "7": 6
  "8": 7
  "9": 8
}
type PlusOneMap = {
  "0": 1
  "1": 2
  "2": 3
  "3": 4
  "4": 5
  "5": 6
  "6": 7
  "7": 8
  "8": 9
  "9": 0
}

type StringToArray<T extends string> = T extends `${infer F}${infer R}`
  ? [F, ...StringToArray<R>]
  : []
type NumberToString<T extends number> = `${T}`

type RemoveStartWithZeros<S extends string> = S extends "0"
  ? S
  : S extends `${0}${infer Rest}`
    ? `${RemoveStartWithZeros<Rest>}`
    : S

type ReverseString<S extends string> = S extends `${infer First}${infer Rest}`
  ? `${ReverseString<Rest>}${First}`
  : ""

type RemoveUnit<S extends string> = S extends `${infer F}${infer R}`
  ? F extends `${number}`
    ? `${F}${RemoveUnit<R>}`
    : `${RemoveUnit<R>}`
  : S

type Initial<S extends string> = ReverseString<S> extends `${infer _}${infer Rest}`
  ? `${ReverseString<Rest>}`
  : S

type MinusOneForString<S extends string, T extends string = Initial<S>> = S extends `${T}${infer Last extends NumberLiteral}`
  ? Last extends '0'
    ? `${MinusOneForString<T>}${MinusOneMap[Last]}`
    : `${T}${MinusOneMap[Last]}`
  : never

type PlusOneForString<S extends string, T extends string = Initial<S>> = S extends `${T}${infer Last extends NumberLiteral}`
  ? Last extends '9'
    ? T extends '9'
      ? `${1}${PlusOneForString<T>}${PlusOneMap[Last]}`
      : `${PlusOneForString<T>}${PlusOneMap[Last]}`
    : `${T}${PlusOneMap[Last]}`
  : S

type GetSignSymbol<T extends string> = T extends `${infer _F extends '-'}${infer _L extends number}`
  ? '-'
  : '+'

type ParseInt<T extends string> =
  RemoveStartWithZeros<T> extends `${infer Digit extends number}`
    ? Digit
    : never

type MinusOne<T extends number, S = GetSignSymbol<`${T}`>> = T extends 0
  ? -1
  : S extends '+'
    ? ParseInt<MinusOneForString<NumberToString<T>>>
    : ParseInt<PlusOneForString<NumberToString<T>>>

// string To Array
type _02257a = StringToArray<"abd">
type _02257b = StringToArray<"">
type _02257d = StringToArray<"123">

type _02257e = NumberToString<123>

// Remove Leading Zeros
type _02257f = RemoveStartWithZeros<"00980">

// ParseInt
type _02257g = ParseInt<"001230">
type _02257h = ParseInt<"">

// ReversString
type _02257i = ReverseString<"0123">

// RemoveReversUnit
type _02257k = RemoveUnit<'123n'>

// Initial
type _02257j = Initial<'1234n'>

// MinusOneString
type _02257l = MinusOneForString<'100'>

// PlusOneMap
type _02257m = PlusOneForString<'-199999999999'>

// Sign
type _02257n = GetSignSymbol<'123'>

type _02257c = MinusOne< -1999 >

Expect<Equal<MinusOne<1>, 0>>
Expect<Equal<MinusOne<55>, 54>>
Expect<Equal<MinusOne<3>, 2>>
Expect<Equal<MinusOne<100>, 99>>
Expect<Equal<MinusOne<1101>, 1100>>
Expect<Equal<MinusOne<0>, -1>>
Expect<Equal<MinusOne<9_007_199_254_740_992>, 9_007_199_254_740_991>>

Solution by HaoxueAllen #26504

// 你的答案
type MinusOne<T, Arr extends string[] = []> =  T extends [...Arr, '']['length'] ? Arr['length'] : MinusOne<T, [...Arr, '']>

Solution by Hencky #26496

Works with arbitrary positive and negative integers by converting to string array of digits and implementing the maths directly on that. A negative number is detected and incremented by 1 and then negated.

type MinusMap = {
  "0": "9";
  "1": "0";
  "2": "1";
  "3": "2";
  "4": "3";
  "5": "4";
  "6": "5";
  "7": "6";
  "8": "7";
  "9": "8";
};
type PlusMap = {
  "9": "0";
  "0": "1";
  "1": "2";
  "2": "3";
  "3": "4";
  "4": "5";
  "5": "6";
  "6": "7";
  "7": "8";
  "8": "9";
};
type Digit = keyof MinusMap;

type StringToArray<S extends string> = S extends `${infer Head}${infer Tail}`
  ? [Head, ...StringToArray<Tail>]
  : [];

type Join<S extends string[]> = S extends [
  infer H extends string,
  ...infer T extends string[]
]
  ? `${H}${Join<T>}`
  : "";

type ParseInt<S extends string> = S extends `0${infer N extends number}`
  ? N
  : S extends `${infer N extends number}`
  ? N
  : never;

type _MinusOne<
  Digits extends Digit[],
  Carry extends Digit[] = []
> = Digits extends [
  ...infer FirstDigits extends Digit[],
  infer LastDigit extends Digit
]
  ? LastDigit extends "0"
    ? _MinusOne<FirstDigits, ["9", ...Carry]>
    : [...FirstDigits, MinusMap[LastDigit], ...Carry]
  : Carry;

type _PlusOne<
  Digits extends Digit[],
  Carry extends Digit[] = []
> = Digits extends [
  ...infer FirstDigits extends Digit[],
  infer LastDigit extends Digit
]
  ? LastDigit extends "9"
    ? FirstDigits extends []
      ? ["1", "0", ...Carry]
      : _PlusOne<FirstDigits, ["0", ...Carry]>
    : [...FirstDigits, PlusMap[LastDigit], ...Carry]
  : Carry;

type SplitNumber<N extends number> = `${N}` extends `-${infer D}`
  ? ["-", StringToArray<D>]
  : ["+", StringToArray<`${N}`>];

type MinusOne<N extends number> = N extends 0
  ? -1
  : ParseInt<
      SplitNumber<N> extends [infer Sign, infer Digits extends Digit[]]
        ? Join<
            Sign extends "-" ? ["-", ..._PlusOne<Digits>] : _MinusOne<Digits>
          >
        : never
    >;

type moreCases = [
  Expect<Equal<MinusOne<-1>, -2>>,
  Expect<Equal<MinusOne<-9>, -10>>,
  Expect<Equal<MinusOne<-99>, -100>>
];

Solution by dexmo007 #26078

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 extends string, Acc extends string = ''> = T extends `${infer Head}${infer Tail}`
  ? Tail extends ''
    ? [head: Acc, last: Head]
    : _GetHeadAndLast<Tail, `${Acc}${Head}`>
  : never
type GetHead<T extends string> = _GetHeadAndLast<T>[0]
type GetLast<T extends string> = _GetHeadAndLast<T>[1]

type ToNumber<T extends string> = 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

Solution by isntkyu #25841