숫자(항상 양수이다)를 받는 타입이 주어진다. 그 타입은 1 작은 number를 반환해야 한다
Given a number (always positive) as a type. Your type should return the number decreased by one.
type DigitMinusOne<N extends number,Length extends any[]=[]>=
[N,...Length]['length'] extends N?
Length['length']
:DigitMinusOne<N,[1,...Length]>
type StringToNumberTuple<S extends string,Result extends number[]=[]>=
S extends `${infer Num extends number}${infer Right}`?
StringToNumberTuple<Right,[...Result,Num]>
:Result
type Join<Arr extends number[],Result extends string = ''>=
Arr extends [infer First extends number,...infer Right extends (number)[]]?
Join<Right,`${Result}${First}`>
:Result;
type StringToNumber<S extends string>=
S extends `${infer Num extends number}`?
Num
:0
type TrimZero<T extends number[]>=
T extends [infer Zero extends 0,...infer RightTuple extends number[]]?
TrimZero<RightTuple>
:T
type NumberTupleMinusOne<T extends number[],RightTuple extends number[]=[]>=
T extends [...infer LeftTuple extends number[],infer Target extends number]?
Target extends 0?
NumberTupleMinusOne<LeftTuple,[9,...RightTuple]>
:TrimZero<[...LeftTuple,DigitMinusOne<Target>,...RightTuple]>
:TrimZero<RightTuple>
type MinusOne<T extends number> = StringToNumber<Join<NumberTupleMinusOne<StringToNumberTuple<`${T}`>>>>
엄청 큰 수가 타입으로 들어가는 반례를 없애기 위해 다음과 같은 방식을 취했다
1. 숫자를 문자열로 바꾼다
2. 1의 결과를 문자열을 number[] 형태로 바꾼다
3. 2의 결과를 맨 뒤에서 부터 확인하며 1 줄여나간다(이때 받아 내림을 구현한다)
이때 0이 맨 앞에 가는 경우 4의 결과가 number가 되므로 0이 된다
4. 3의 결과 튜플을 Join 한다
5. 4의 결과를 number 형태로 바꾼다
type ParseInt<T extends string> = T extends `${infer Digit extends number}` ? Digit : never
type ReverseString<S extends string> = S extends `${infer First}${infer Rest}` ? `${ReverseString<Rest>}${First}` : ''
type RemoveLeadingZeros<S extends string> = S extends '0' ? S : S extends `${'0'}${infer R}` ? RemoveLeadingZeros<R> : S
type InternalMinusOne<
S extends string
> = S extends `${infer Digit extends number}${infer Rest}` ?
Digit extends 0 ?
`9${InternalMinusOne<Rest>}` :
`${[9, 0, 1, 2, 3, 4, 5, 6, 7, 8][Digit]}${Rest}`:
never
type MinusOne<T extends number> = ParseInt<RemoveLeadingZeros<ReverseString<InternalMinusOne<ReverseString<`${T}`>>>>>
접근 방법이 굉장히 비슷한 풀이였다. 숫자 배열을 사용한다는 점이 인상깊었다.
큰 수가 들어가지 않는다는 가정하에서는 내 풀이의 DigitMinusOne과 작동 방식이 비슷한 풀이가 많았다.