티스토리 뷰

 

아래 글에 이어서 작성되었습니다.

 

 

[TS] Greater Than 타입 구현해보기 - 1

타입 챌린지 Greater Than에 도전합니다. type-challenges/questions/04425-medium-greater-than/README.md at main · type-challenges/type-challenges Collection of TypeScript type challenges with online judge - type-challenges/type-challenges github.c

munak.tistory.com

 


 

서론

이전글에서 배열을 이용해 Greater Than 타입을 구현했었습니다.

하지만 기본적으로 타입스크립트에서는 너무 많은 재귀가 발생하는 것을 막고 있기 때문에 999까지만 비교할 수 있었는데요.

이번글에서는 그 이상의 큰 숫자도 가능하도록 타입을 다시 짜보려고 합니다.

 

 

음... 이런 타입까지 짜야하나요? 🤔 

실제로 이런 타입을 사용하는지, 필요한지는 저도 의문이 들 때가 많습니다만

저는 타입을 잘 이해하는 데에 초점을 두고 타입챌린지에 임하고 있습니다.  나중에 필요한 타입을 뚝딱 만들어 낼 수 있게요.

 

 

타입에서 큰 수를 다루는 방법 

타입에서 큰 수를 다루는 방법은 크게 2가지가 있습니다.

 

1. 문자열로 변환 후 파싱하기 ( ex. 123 → '123' )

2. 배열로 변환 후 요소로 이용하기 ( ex. 123 → ['1', '2', '3'] )

 

저는 2번으로 구현해 보려고 합니다. 

이전글에서 사용한 배열과 인덱스를 사용한 정수 비교를 그대로 이용하기 위해서요.

 

 

 

시나리오를 작성해 볼까요?  비교할 숫자를 X와 Y라고 해보겠습니다. (음수는 고려하지 않습니다.)

비교에 앞서, 숫자 X과  Y를 각각의 배열 X', Y'로 변환합니다.

 

1. X와 Y의 자릿수가 다를 경우

자릿수가 다른 경우에는 비교하기가 쉬울 겁니다. 두 배열의 길이가 다를 테니까요.

이전글과 같이 X'[Y'['length']]을 이용하는 방법을 사용하면 될 것 같습니다.

 

2. X와  Y의 자릿수가 같은 경우

자리수가 같은 경우에는 최고 자리의 수부터, 배열의 요소를 앞에서부터 하나씩 꺼내 비교해 보도록 하겠습니다.

 

 

 

구현하기

그럼 필요한 유틸 함수부터 작성해 보겠습니다.

 

StringToArray

타입에서 숫자를 자릿수대로 분리하려면 먼저 문자열로 변환해야 합니다. 

그 뒤에 infer 키워드를 사용해 하나씩 분리하면 됩니다. 따라서 StringToArray<T>의 T는 string 타입이 됩니다. 

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

 

 

사용 시에는 아래와 같이 숫자를 템플릿 문자열로 바꾸어서 전달해 주어야 합니다.

// type test = ["1", "2", "3"]
type test = StringToArray<`${123}`>

 

 

 

_GreaterThan

변환된 배열 T와  U를 받아 본격적으로 비교를 하는 내부 타입을 작성해 보겠습니다.

type _GreaterThan<T extends any[], U extends any[]> = T와 U를 비교합니다.

type GreaterThan<T extends number, U extends number> = _GreaterThan<StringToArray<`${T}`>, StringToArray<`${U}`>>;

 

_GreaterThan은 StringToArray로 변환된 문자 배열 T와 U를 받아 비교 결과를 반환하는 내부타입입니다.

GreaterThan<50, 10>이라면 _GreaterThan<['5', '0'], ['1', '0']>이 전달되겠네요.

*문자열로 변환한 뒤 분할했기 때문에 문자 배열이 됩니다!

 

시나리오 내용을 글로 추가하면 다음과 같습니다.

type _GreaterThan<T extends any[], U extends any[]> = T가 U보다 길이가 작거나 같은가
  ? U가 T보다 작거나 같은가
    ? T와 U의 길이가 같다.
    : U가 더 길다.
  : T가 더 길다.

type GreaterThan<T extends number, U extends number> = _GreaterThan<StringToArray<`${T}`>, StringToArray<`${U}`>>;

 

 

LengthThen

T와 U의 길이를 비교하는 타입 LengthThen을 추가해 보겠습니다. 

type LengthThen<X extends any[], Y extends any[]> = X[Y['length']] extends undefined ? false : true

type _GreaterThan<T extends any[], U extends any[]> = T가 U보다 길이가 작거나 같은가
  ? U가 T보다 작거나 같은가
    ? T와 U의 길이가 같다.
    : U가 더 길다.
  : T가 더 길다.

type GreaterThan<T extends number, U extends number> = _GreaterThan<StringToArray<`${T}`>, StringToArray<`${U}`>>;

 

T의 길이가 U의 길이보다 더 길다면 true를 반환하고, 같거나 작다면 false를 반환합니다.

 

정리하면 다음과 같이 됩니다.

type LengthThen<X extends any[], Y extends any[]> = X[Y['length']] extends undefined ? false : true

type _GreaterThan<T extends any[], U extends any[]> = LengthThen<T, U> extends false
  ? LengthThen<U, T> extends false
    ?  T와 U의 길이가 같다.
    : false
  : true;

type GreaterThan<T extends number, U extends number> = _GreaterThan<StringToArray<`${T}`>, StringToArray<`${U}`>>;

 

 

 

NumberThen 

길이가 같은 경우(자릿수가 같은 경우)에는 NumberThen으로 넘겨 처리해 보도록 하겠습니다.

type _GreaterThan<T extends any[], U extends any[]> = LengthThen<T, U> extends false
  ? LengthThen<U, T> extends false
    ? NumberThen<T, U> // T와 U의 길이가 같다.
    : false
  : true;

 

 

 

NumberThen은 배열 T와 U를 넘겨받습니다.

type NumberThen<T extends any[], U extends any[]> = any

 

 

자리수가 같은 경우에는 최고 자리의 수부터, 배열의 요소를 앞에서부터 하나씩 꺼내 비교해 보도록 하겠습니다.

 

이를 위해 T, U의 첫 번째 요소들을 가져옵니다. 각 요소는 소문자 t와 u로 명시하였습니다.

type NumberThen<T extends any[], U extends any[]> = T extends [infer t extends string, ...infer TR]
  ?  U extends [infer u  extends string, ...infer UR]
    ? t와 u가 존재합니다. t와 u를 비교합니다.
    : t는 존재하나 u가 존재하지 않습니다.
  : t가 존재 하지 않습니다.

 

 

이때 ` t는 존재하나 u가 존재하지 않는 경우`는 발생하지 않습니다.

T와 U의 길이는 무조건 같으니까요. 따라서 never가 됩니다.

type NumberThen<T extends any[], U extends any[]> = T extends [infer t extends string, ...infer TR]
  ?  U extends [infer u  extends string, ...infer UR]
    ? t와 u가 존재합니다. t와 u를 비교합니다.
    : never
  : t가 존재 하지 않습니다.

 

 

 

t와 u를 비교하는 과정을 글로 추가해 보았습니다.

type NumberThen<T extends any[], U extends any[]> = T extends [infer t extends string, ...infer TR]
   ? U extends [infer u  extends string, ...infer UR]
      ? t와 u가 같은가
        ? 맞다면, 다음 요소들을 비교합니다. (재귀)
        : 아니라면, t와 u를 비교한 결과를 반환합니다.
      : never
  : t가 존재 하지 않습니다.

 

 

 

t와 u가 같은 경우라면 비교할 필요가 없겠죠. 다음 요소를 검사하기 위해 NumberThen를 재귀호출 합니다. 

type NumberThen<T extends any[], U extends any[]> = T extends [infer t extends string, ...infer TR]
   ? U extends [infer u  extends string, ...infer UR]
      ? t extends u
        ? NumberThen<TR, UR>
        : 아니라면, t와 u를 비교한 결과를 반환합니다.
      : never
    : false

 

`t가 존재하지 않는 경우`는 재귀 호출로 마지막 요소까지 전부 비교를 한 경우입니다.

마지막 요소까지 확인했으나 전부 같았다는 뜻이므로, T와 U가 동일합니다. false를 반환하면 됩니다.

 

 

 

 

같지 않다면 무조건 큰 쪽과 작은 쪽으로 나뉜다는 뜻이겠죠?

따라서 t와 u의 비교 결과를 그대로 반환하면 됩니다.

 

문자 t와 u를 숫자로 변환한 뒤, 해당 길이를 가지는 배열을 만들어 LengthThen을 이용해 비교하는 방법을 사용해 보겠습니다.

type NumberThen<T extends any[], U extends any[]> = T extends [infer t extends string, ...infer TR]
   ? U extends [infer u  extends string, ...infer UR]
      ? t extends u
        ? NumberThen<TR, UR>
        : LengthThen< 길이 t를 가지는 배열, 길이 u를 가지는 배열 >
      : never
    : false

 

 

StringToNumber

문자를 숫자로 변환하는 타입을 추가했습니다.

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

 

문자열에서 숫자를 추출하는 타입입니다.

 

 

NewArray

이어서 이전글에서 사용한 NewArray를 추가합니다.

type NewArray<T extends number, R extends any[] = []> = R['length'] extends T ? R : NewArray<T, [...R, 0]>;

 

숫자 T를 전달받아 해당 길이만큼 0으로 채워진 배열 타입을 반환합니다.

 

 

 

추가한 타입들을 이용해 코드를 작성하면 구현완료입니다 🎉

type NumberThen<T extends any[], U extends any[]> = T extends [infer t extends string, ...infer TR]
   ? U extends [infer u  extends string, ...infer UR]
      ? t extends u
        ? NumberThen<TR, UR>
        : LengthThen<NewArray<StringToNumber<t>>, NewArray<StringToNumber<u>>>
      : never
    : false

 

 

 

아래 playground에서 테스트해 볼 수 있습니다. ✌️

 

TS Playground - An online editor for exploring TypeScript and JavaScript

The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.

www.typescriptlang.org

 

 

 

감사합니다.

 


 

 

공부한 내용을 복습/기록하기 위해 작성한 글이므로 내용에 오류가 있을 수 있습니다.

댓글
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Total
Today
Yesterday