카테고리 없음

TS 타입 챌린지 스터디 - 2

LucetTin5 2025. 1. 26. 12:07

TS Types - Week 2

Easy-189-Awaited

최종적으로 반환되는 타입을 추출하는 타입

type MyAwaited<T> = T extends null | undefined
  ? T
  : T extends object & { then(onfulfilled: infer F, ...args: infer _): any }
  ? F extends (value: infer V, ...args: infer _) => any
    ? MyAwaited<V>
    : never
  : T;
  • T extends null | undefined : T가 null 또는 undefined인 경우, T를 반환합니다.
  • T extends object & { then(onfulfilled: infer F, ...args: infer _): any } : T가 object이고 then 메서드를 가지고 있는 경우(Promise 타입인 경우), 해당 then 메서드의 첫 번째 매개변수(onfulfilled)를 추출하여 F로 정의합니다.
  • F extends (value: infer V, ...args: infer _) => any : onfulfilled 메서드의 첫 번째 매개변수 value를 추출하여 V로 정의한 경우, MyAwaited<V>를 반환합니다.
  • T : 위의 조건을 모두 만족하지 않는 경우, T를 반환합니다. (Promise 타입이 아니게 된, 최종적으로 resolved된 타입을 반환)

Promise 타입

Promise Constructor

  • 이는 Promise 생성자 함수의 타입
  • new, resolve, reject 메서드를 가지고 있는 객체를 정의하는 타입
const promise = new Promise<string>((resolve, reject) => {
  resolve("Hello, world!");
});

Promise<T>

  • 이는 Promise 생성자의 제네릭 타입으로, 생성된 Promise 객체의 타입
  • then, catch, finally 메서드를 가지고 있는 객체를 정의;
  • then 메서드는 onfulfilled, onrejected 매개변수를 가지고 있는 함수를 정의
const promise = new Promise<string>((resolve, reject) => {
  resolve("Hello, world!");
});

const onFulfilled = (value: any) => {
  // ...
};
const onRejected = (reason: any) => {
  // ...
};
const onFinally = () => {
  // ...
};

promise.then(onFulfilled, onRejected).catch(onRejected).finally(onFinally);

Easy-268-Easy-If

type If<C extends boolean, T, F> = C extends true ? T : F;
  • C: Condition
  • T: True 시 반환되는 타입
  • F: False 시 반환되는 타입

Easy-533-Easy-Concat

type Concat<T extends any[], U extends any[]> = [...T, ...U];
  • T, U: 각각 배열 타입
  • spread operator를 이용하여 배열을 합치는 타입

Easy-898-Easy-Includes

처음 풀이

type Includes<T extends any[], U> = U extends T[number] ? true : false;
  • T: 배열 타입
  • U: 배열 타입의 요소 타입
  • T[number] : T의 요소들의 타입
  • U extends T[number] : U가 T의 요소들의 타입 중 하나라면 true, 아니면 false

두번째 풀이

  • 테스트 케이스 중 정답과 다른 케이스들이 있음
// 실패하는 케이스들
type A = Includes<[boolean, 2, 3, 5, 6, 7], false>;
type B = Includes<[{ a: "A" }], { readonly a: "A" }>;
type C = Includes<[{ readonly a: "A" }], { a: "A" }>;
type D = Includes<[1], 1 | 2>;
type E = Includes<[1 | 2], 1>;
  • 타입 추론 과정에서 문제가 있는 것으로 보임
  • A: U extends T[number] 과정에서 false extends boolean에 의해 true로 추론됨, 이는 원하는 결과가 아님
  • B, C: 읽기전용 속성을 가지고 있는 객체와 읽기전용 속성을 가지고 있지 않은 객체가 동일한 타입으로 추론됨
  • D, E: Union 타입과 관련된 문제가 있음
    • D의 경우 1 | 2 extends 1은 false이지만, U extends T[number]에서는 1 | 2 extends 1로 잘못 비교됨
    • E의 경우 배열 요소가 Union 타입일 때 1 extends 1 | 2는 true이지만, 정확한 타입 일치 여부를 확인해야 함

Union 관련, 객체 타입 비교, boolean 타입 비교에서 문제가 있는 것으로 나타남
정확한 타입의 비교가 필요하다는 것을 알 수 있음

참고 - TS PR
참고 - 블로그

type Equal<A, B> = (<T>() => T extends A ? 1 : 2) extends <P>() => P extends B
  ? 1
  : 2
  ? true
  : false;

두 타입이 동일한지 확인하는 타입으로 이를 이용하여 타입 비교를 보다 정확하게 할 수 있음

type Includes<T extends any[], U> = T extends [infer F, ...infer R]
  ? Equal<F, U> extends true
    ? true
    : Includes<R, U>
  : false;
  • 배열의 요소를 순회하면서 요소와 U의 타입을 비교
  • 비교 결과가 true라면 true를 반환, 아니라면 다음 요소와 비교
  • 모든 요소를 비교한 후에도 일치하는 요소가 없다면 false를 반환

Easy-3057-Easy-Push

type Push<T extends unknown[], U> = [...T, U];
  • T: 배열 타입
  • U: 배열 타입의 요소 타입
  • spread operator를 이용하여 배열의 마지막에 U를 추가하는 타입
  • unknown의 사용: 실제 사용 시 unknown 타입은 모든 타입일 수 있기에 타입 추론, 검사 과정을 요구함
    하지만 any 타입은 모든 타입을 포함하기에 타입 추론, 검사 과정을 요구하지 않아 런타임 에러 발생 가능

Easy-3060-Easy-Unshift

type Unshift<T extends unknown[], U> = [U, ...T];
  • T: 배열 타입
  • U: 배열 타입의 요소 타입
  • spread operator를 이용하여 배열의 맨 앞에 U를 추가하는 타입

Easy-3312-Easy-Parameters

type MyParameters<T extends (...args: any[]) => unknown> = T extends (
  ...args: infer P
) => unknown
  ? P
  : never;
  • 첫번째 제너릭의 ...args의 타입에 unknown[]을 사용할 수 없는 이유
    • 함수의 매개변수가 갖는 반공변성으로 인해 unknown[]으로 선언된 args는 unknown[]으로 구성된 매개변수 집합만이 타입 일치 조건을 만족하게됨
type MyFn = (...args: unknown[]) => unknown;

// Valid example: 매개변수가 unknown 타입
const func1: MyFn = (...args: unknown[]) => {
  return args.length;
};

// Valid example: 매개변수가 unknown으로 더 일반적임
const func2: MyFn = (...args: any[]) => {
  return args[0];
};

// Invalid example: 특정 타입 매개변수를 요구
const func3: MyFn = (a: number, b: string) => {
  return a + b; // Error: 'T'는 unknown 매개변수만 허용
};
  • return 타입은 위 규칙에 영향을 받지 않고 추출하는 P와 연관이 없기에 문제가 되지 않음

참고 - 타입스크립트의 공변성, 반공변성