프로그래밍/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개는 타입 챌린지를 풀다 보면 많이들 쓰게 되는 타입들이다.
- Length는 사실 제네릭 T[’length’]의 접근을 축약한 것이기 때문에 크게 어렵지 않을 것이다.
- Push는 Tuple T를 받아 P를 담은 새 튜플로 추론되는 타입이다.
- NTuple은 튜플 T가 N만큼의 크기가 될 때까지 재귀적으로 Push하는 타입이다.
- 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으로 바꾸고 아니면 그대로 한다.
이를 재귀적으로 돌면서 계속 반복하는 것이다.
반응형