kakasoo

[Type-Challenge] 04518-medium-fill 본문

프로그래밍/TypeScript

[Type-Challenge] 04518-medium-fill

카카수(kakasoo) 2024. 3. 24. 22:37
반응형
type Length<T extends any[]> = T['length'];
type Push<T extends any[], P> = [...T, P];
type NTuple<N extends number, T extends any[] = []> = Length<T> extends N ? T : NTuple<N, Push<T, any>>;
type Add<N1 extends number, N2 extends number> = Length<[...NTuple<N1>, ...NTuple<N2>]>;

 

이렇게 4개는 타입 챌린지를 풀다 보면 많이들 쓰게 되는 타입들이다.

  1. Length는 사실 제네릭 T[’length’]의 접근을 축약한 것이기 때문에 크게 어렵지 않을 것이다.
  2. Push는 Tuple T를 받아 P를 담은 새 튜플로 추론되는 타입이다.
  3. NTuple은 튜플 T가 N만큼의 크기가 될 때까지 재귀적으로 Push하는 타입이다.
  4. Add는 N1, N2를 그 크기만큼의 튜플로 만든 다음, 그 튜플을 합쳐 다시 Length를 구하는 방식으로 두 수의 합을 구하는 타입이다.

이 네 가지 타입이 자주 쓰이는 것은, 그만큼 타입챌린지에 튜플을 이용한 타입 문제가 많이 나오기 때문일 것이다.

 

// N1이 큰 경우에만 true
type IsBiggerOrEqual<N1 extends number, N2 extends number> = NTuple<N1> extends [...NTuple<N2>, ...any[]] ? true : false;
type IsBigger<N1 extends number, N2 extends number> = IsBiggerOrEqual<N1, N2> extends true ? N1 extends N2 ? false : true : false;

 

Fill 문제를 풀기 위해서는 현재 인덱스가 문제에 주어진 Start 이상 End 미만인지를 검증할 필요가 있다.

따라서 이상, 미만을 체크하기 위해 두 가지 타입을 만든다.

IsBiggerOrEqual은 이름만큼이나 직관적인 타입인데 N1, N2 튜플이 있다고 할 때,

N1 튜플이 N2 튜플과 몇 개 요소가 더해진 것과 동일한 튜플인지 비교하는 타입이다. ( = any로만 이루어진 튜플이기 때문에 가능하다 )

IsBigger 타입은 'N1이 N2보다 크거나 같은' 상황에서 '값이 같은' 경우를 제외하여 오로지 큰 경우만 남기는 타입이다.

이로써 인덱스를 비교할 준비는 되었다.

 

// 두 값 사이에 속한 경우에만 true (TargetNumber가 Minimum 이상, Maximum 미만인 경우)
type Between<TargetNumber extends number, Minimum extends number, Maximum extends number> = 
    IsBiggerOrEqual<TargetNumber, Minimum> extends true 
      ? IsBigger<Maximum, TargetNumber> extends true
        ? true
        : false
      : false

 

마지막으로 Between 타입을 구현한다.

Between 타입이 true로 추론되는 경우는 타겟이 되는 숫자가 Minimum 이상이고, Maximum보다는 미만인 경우이다.

이제 TargetNumber의 위치로 인덱스를 주기만 하면 된다.

 


type ToNumber<T> = T extends number ? T : never;

type Fill<
  T extends unknown[],
  N,
  Start extends number = 0,
  End extends number = T['length'],
  CurrentIndex extends number = 0
> = T extends [infer First, ...infer Rest] 
  ? Between<CurrentIndex, Start, End> extends true 
    ? [N, ...Fill<Rest, N, Start, End, ToNumber<Add<CurrentIndex, 1>>>] 
    : [First, ...Fill<Rest, N, Start, End, ToNumber<Add<CurrentIndex, 1>>>]
  : []

 

따라서 Fill 타입은 위처럼 구현한다.

T가 First와 나머지 배열로 이루어진 배열이라고 가정할 때, CurrentIndex가 Start, End 사이이면 N으로 바꾸고 아니면 그대로 한다.

이를 재귀적으로 돌면서 계속 반복하는 것이다.

 

 

type-challenges/questions/04518-medium-fill at 9bae5a3e540029ad523e46486829654aeb70ef62 · type-challenges/type-challenges

Collection of TypeScript type challenges with online judge - type-challenges/type-challenges

github.com

 

반응형