dev-hamster

[type-challenges] easy 06-09 본문

타입스크립트/type-challenges

[type-challenges] easy 06-09

dev-hamster 2024. 10. 11. 20:11

Exclude

T에서 U에 할당할 수 있는 타입을 제외하는 내장 제네릭 Exclude를 사용하지 않고 구현하는 문제이다.

  • Exclude: Exclude<T, U> 는 U에 할당할 수 있는 타입을 제외한다. 차집합으로 이해할 수 있다.
  • 분배법칙: 조건부 타입에서 분배법칙이 적용된다. 예를 들어 A | B | C extends U ? X : YA extends U ? X : Y, B extends U ? X : Y, C extends U ? X : Y 가 된다. T extends U ? X : Y 에서 T 유니언 타입은 개별 구성 요소를 확인한다.

T가 U에 속한다면, never를 반환한다.

type MyExclude<T, U> = T extends U ? never : T;

Awaited

Promise와 같은 타입에 감싸인 타입이 있을 때, 안에 감싸인 타입이 무엇인지 가르키는 타입을 구현하는 문제이다.

Promise에 감싸진 리턴 타입을 반환하기 위해 T가 Promise라면 Promise를 재귀적으로 풀어주고 그렇지 않다면 T를 반환하면 된다.

type MyAwaited<T> = T extends PromiseLike<infer D> ? MyAwaited<D> : T;

예를 들어 X에서 Promise로 감싸진 타입을 가져올 때 다음처럼 동작한다.

type MyAwaited<T> = T extends PromiseLike<infer D> ? MyAwaited<D> : T;

type X = Promise<string>
type Foo = MyAwaited<X> // string

MyAwaited<Promise<string>>PromiseLike<infer D>타입에 해당하므로 다시 MyAwaited<D> 을 호출한다. 여기서 infer D 로 string을 추론한다. MyAwaited<string>PromiseLike타입이 아니므로 타입 string을 반환한다.

infer

조건부 타입에서 infer 키워드를 이용해 새로운 타입 변수를 선언적으로 정의할 수 있다. type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

PromiseLike

Promise가 아닌 PromiseLike를 사용하는 이유는 아래 테스트 케이스가 존재하기 때문이다.

type T = { then: (onfulfilled: (arg: number) => any) => any }

PromiseLike타입에는 후속처리 메서드인 then만 가지고 있다. 이를 통해 커스텀 비동기 처리를 유연하게 정의할 수 있다.

// https://github.com/microsoft/TypeScript/blob/main/src/lib/es5.d.ts

interface PromiseLike<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(
        onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, 
        onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
      ): PromiseLike<TResult1 | TResult2>;
}

If

조건 C가 참이면 A, 아니면 B를 반환하는 타입을 구현하자.

C는 true, false만 가능하도록 boolean 타입으로 제한하고 C 조건에 따라 T, F를 리턴하면 된다.

type If<C extends boolean, T, F> = C extends true ? T : F;

Concat

Array.concat 함수를 타입 시스템에서 구현하자. 인수를 왼쪽부터 concat한 새로운 배열을 반환하자.

T와 U를 concat할 수 있도록 T와 U를 배열로 타입을 제한했다. 요소의 타입으로 이루어진 튜플을 반환할 수 있도록 T[number] 를 했다. 그러나 T[number]는 배열의 요소 타입으로 이뤄진 유니언 타입을 추출한다. 그리고 빈 배열인 경우 never 타입이 반환되므로 오답이다.

// 오답
type Concat<T extends unknown[], U extends unknown[]> = [
  T extends [] ? never : T[number], 
  U extends [] ? never : U[number] 
]; 

type Foo = Concat<['1', 2, 3], []>; // [2 | 3 | "1", never]

T와 U를 배열타입으로 제한했으므로 열거 가능하다.

// 정답
type Tuple = readonly unknown[];
type Concat<T extends Tuple, U extends Tuple> = [...T, ...U];

type Foo = Concat<['1', 2, 3], []>; //  ["1", 2, 3]            

'타입스크립트 > type-challenges' 카테고리의 다른 글

[type-challenges] medium 10-13  (0) 2024.10.22
[type-chllenges] medium 05-08  (2) 2024.10.22
[type-challenges] medium 01-04  (0) 2024.10.17
[type-challengs] easy 10-14  (0) 2024.10.12
[type-challenges] easy 01-05  (2) 2024.10.11