题目描述
给定一个正整数作为类型的参数,要求返回的类型是该数字减 1。
例如:
type Zero = MinusOne<1> // 0
type FiftyFour = MinusOne<55> // 54
题解
方案一
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'
type cases = [
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>>,
]
// ============= Your Code Here =============
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}`>>>>>;
解释
1.ParseInt<T>:
-
将字符串类型的数字转换为数字类型
-
如果
T是一个数字字符串,返回对应的数字类型;否则返回T
2.ReverseStr<T>:
-
将字符串
T颠倒 -
递归地将字符串的每个字符移到末尾,直到字符串为空
3.RemoveLeadingZero<T>:
-
移除字符串
T开头的0 -
如果字符串以
0开头,递归地移除开头的0;如果字符串为空,返回0
4.InnerMinusOne<T>:
-
对字符串
T的第一个字符减一 -
如果第一个字符是
0,递归地对剩余字符串减一,并在前面加上9 -
如果第一个字符不是
0,返回减一后的字符和剩余字符串
5.InnerPlusOne<T>:
-
对字符串
T的第一个字符加一 -
如果第一个字符是
9,递归地对剩余字符串加一,并在前面加上0 -
如果第一个字符不是
9,返回加一后的字符和剩余字符串
6.MinusOne<T>:
-
如果
T是0,返回-1 -
如果
T是负数,对负数部分加一,然后返回负数 -
如果
T是正数,对正数部分减一,然后返回结果
推导过程
1.MinusOne<1>:
-
ReverseStr<'1'>='1' -
InnerMinusOne<'1'>='0' -
ReverseStr<'0'>='0' -
RemoveLeadingZero<'0'>='0' -
ParseInt<'0'>=0 -
最终结果是
0
2.MinusOne<55>:
-
ReverseStr<'55'>='55' -
InnerMinusOne<'55'>='45' -
ReverseStr<'45'>='54' -
RemoveLeadingZero<'54'>='54' -
ParseInt<'54'>=54 -
最终结果是
54
3.MinusOne<100>:
-
ReverseStr<'100'>='001' -
InnerMinusOne<'001'>='990' -
ReverseStr<'990'>='099' -
RemoveLeadingZero<'099'>='99' -
ParseInt<'99'>=99 -
最终结果是
99
方案二
type ParseInt<T extends string> =
T extends `${infer Digit extends number}`
? Digit
: T;
type Expand<T extends number | string> =
`${T}` extends `${infer D extends number}${infer V}`
? [D, ...Expand<V>]
: [];
type DigitMinusOne<T extends number[]> =
T extends [...infer U extends number[], infer V extends number]
? V extends 0
? [...DigitMinusOne<U>, 9]
: [...U, [9, 0, 1, 2, 3, 4, 5, 6, 7, 8][V]]
: [];
type DigitPlusOne<T extends number[]> =
T extends [...infer U extends number[], infer V extends number]
? V extends 9
? [...DigitPlusOne<U>, 0]
: [...U, [1, 2, 3, 4, 5, 6, 7, 8, 9, 0][V]]
: [];
type Collapse<T extends number[], N extends number = 0> =
T extends [infer D extends number, ...infer V extends number[]]
?
Collapse<
V,
N extends 0 ? D : `${N}${D}` extends `${infer ND extends number}` ? ND : never
>
: N;
type MinusOne<T extends number> =
T extends 0
? -1
: `${T}` extends `-${infer N}`
? ParseInt<`-${Collapse<DigitPlusOne<Expand<N>>>}`>
: Collapse<DigitMinusOne<Expand<T>>>
解释
1.Expand<T>:
-
将
T转换为数字数组 -
递归地将
T的每个字符转换为数字并存储在数组中
2.DigitMinusOne<T>:
-
对数字数组
T的最后一个数字减一 -
如果最后一个数字是
0,递归地对前面的数字减一,并在末尾加上9 -
如果最后一个数字不是
0,返回减一后的数字和前面的数字
3.DigitPlusOne<T>:
-
对数字数组
T的最后一个数字加一 -
如果最后一个数字是
9,递归地对前面的数字加一,并在末尾加上0 -
如果最后一个数字不是
9,返回加一后的数字和前面的数字
4.Collapse<T>:
-
将数字数组
T转换为数字 -
递归地将数组中的每个数字拼接成一个字符串,并转换为数字
5.MinusOne<T>:
-
如果
T是0,返回-1 -
如果
T是负数,对负数部分加一,然后返回负数 -
如果
T是正数,对正数部分减一,然后返回结果
推导过程
1.MinusOne<1>:
-
Expand<1>=[1] -
DigitMinusOne<[1]>=[0] -
Collapse<[0]>=0 -
最终结果是
0
2.MinusOne<55>:
-
Expand<55>=[5, 5] -
DigitMinusOne<[5, 5]>=[5, 4] -
Collapse<[5, 4]>=54 -
最终结果是
54
3.MinusOne<100>:
-
Expand<100>=[1, 0, 0] -
DigitMinusOne<[1, 0, 0]>=[0, 9, 9] -
Collapse<[0, 9, 9]>=99 -
最终结果是
99
方案三
type Expand<T extends number | string> =
`${T}` extends `${infer D extends number}${infer V}`
? [D, ...Expand<V>]
: [];
type DigitMinusOne<T extends number[]> =
T extends [...infer U extends number[], infer V extends number]
? V extends 0
? [...DigitMinusOne<U>, 9]
: [...U, [9, 0, 1, 2, 3, 4, 5, 6, 7, 8][V]]
: [];
type DigitPlusOne<T extends number[]> =
T extends [...infer U extends number[], infer V extends number]
? V extends 9
? [...DigitPlusOne<U>, 0]
: [...U, [1, 2, 3, 4, 5, 6, 7, 8, 9][V]]
: [1];
type Collapse<T extends number[], N extends number = 0> =
T extends [infer D extends number, ...infer V extends number[]]
?
Collapse<
V,
N extends 0 ? D : `${N}${D}` extends `${infer ND extends number}` ? ND : never
>
: N;
type DigitStep<T extends number[], IsPos extends boolean> =
IsPos extends true
? DigitMinusOne<T>
: DigitPlusOne<T>;
type IsPositive<T extends number> = `${T}` extends `-${number}` | '0' ? false : true;
type Unsigned<T extends number> = `${T}` extends `-${infer N extends number}` ? N : T;
type Signed<T extends number, IsPos extends boolean> =
IsPos extends true ? T : `-${T}` extends `${infer N extends number}` ? N : never;
type MinusOneSigned<T extends number, IsPos extends boolean> =
Signed<Collapse<DigitStep<Expand<T>, IsPos>>, IsPos>;
type MinusOne<T extends number> = MinusOneSigned<Unsigned<T>, IsPositive<T>>;
解释
1.Expand<T>:
-
将
T转换为数字数组 -
递归地将
T的每个字符转换为数字并存储在数组中
2.DigitMinusOne<T>:
-
对数字数组
T的最后一个数字减一 -
如果最后一个数字是
0,递归地对前面的数字减一,并在末尾加上9 -
如果最后一个数字不是
0,返回减一后的数字和前面的数字
3.DigitPlusOne<T>:
-
对数字数组
T的最后一个数字加一 -
如果最后一个数字是
9,递归地对前面的数字加一,并在末尾加上0 -
如果最后一个数字不是
9,返回加一后的数字和前面的数字
4.Collapse<T>:
-
将数字数组
T转换为数字 -
递归地将数组中的每个数字拼接成一个字符串,并转换为数字
5.DigitStep<T, IsPos>:
- 根据
IsPos的值,选择对数字数组T进行减一或加一操作.DigitStep<T, IsPos>:
6.IsPositive<T>:
-
检查数字
T是否为正数 -
如果
T是负数或0,返回false;否则返回true
7.Unsigned<T>:
- 如果
T是负数,返回其绝对值;否则返回T
8.Signed<T, IsPos>:
- 根据
IsPos的值,返回T的正数或负数形式
9.MinusOneSigned<T, IsPos>:
- 对数字
T进行减一操作,并根据IsPos的值返回正数或负数
10.MinusOne<T>:
- 对数字
T进行减一操作
推导过程
1.MinusOne<1>:
-
Unsigned<1>=1 -
IsPositive<1>=true -
Expand<1>=[1] -
DigitStep<[1], true>=DigitMinusOne<[1]>=[0] -
Collapse<[0]>=0 -
Signed<0, true>=0 -
最终结果是
0
2.MinusOne<55>:
-
Unsigned<55>=55 -
IsPositive<55>=true -
Expand<55>=[5, 5] -
DigitStep<[5, 5], true>=DigitMinusOne<[5, 5]>=[5, 4] -
Collapse<[5, 4]>=54 -
Signed<54, true>=54 -
最终结果是
54
3.MinusOne<100>:
-
Unsigned<100>=100 -
IsPositive<100>=true -
Expand<100>=[1, 0, 0] -
DigitStep<[1, 0, 0], true>=DigitMinusOne<[1, 0, 0]>=[0, 9, 9] -
Collapse<[0, 9, 9]>=99 -
Signed<99, true>=99 -
最终结果是
99