02257-medium-minusone

Back

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 = 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 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:

  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