타입 챌린지 2257 - MinusOne

소파의 벨로그·2025년 4월 5일

타입챌린지

목록 보기
45/131

문제 링크

문제

숫자(항상 양수이다)를 받는 타입이 주어진다. 그 타입은 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과 작동 방식이 비슷한 풀이가 많았다.

참고자료

https://github.com/type-challenges/type-challenges/blob/main/questions/02257-medium-minusone/README.md

0개의 댓글