TypeScript·

20 - Promise.all

  • #Type Challenges
  • #TypeScript

Question

Type the function PromiseAll that accepts an array of PromiseLike objects, the returning value should be Promise<T> where T is the resolved result array.

1
const promise1 = Promise.resolve(3);
2
const promise2 = 42;
3
const promise3 = new Promise<string>((resolve, reject) => {
4
setTimeout(resolve, 100, 'foo');
5
});
6
7
// expected to be `Promise<[number, 42, string]>`
8
const p = PromiseAll([promise1, promise2, promise3] as const)
9
10

선행 지식

  1. Variadic Tuple [...T]

    배열 리터럴을 튜플처럼 추론시키는 파라미터 패턴

    1
    const a = [1, 2, 3]; // 배열 리터럴 number[]
    2
    3
    // [...T] 형태의 파라미터 패턴을 사용하여 튜플로 추론하도록 유도
    4
    declare function f<T extends readonly unknown[]>(x: readonly [...T]): T;
    5
    const t = f([1, 2, 3]); // [number, number, number] (환경에 따라 튜플 유지)
    6
    7
    // as const면 확정 튜플
    8
    const t2 = f([1, 2, 3] as const); // [1, 2, 3]
  2. TypeScript에서 배열/튜플 순회

    T[number]는 “요소 타입”을 꺼내는 방식이라, 튜플에 쓰면 각 자리 정보가 사라지고 유니온으로 뭉개진다. 반대로 각 자리(0, 1, 2 …)를 유지하며 변환하려면 Mapped Type으로 keyof T를 순회한다.

    1
    type Input = ["A", "B", "C"];
    2
    3
    // 1. T[number] : 튜플을 깨고 요소들을 유니온으로 뭉침
    4
    type AsUnion = Input[number];
    5
    // 결과: "A" | "B" | "C"
    6
    7
    // 2. T[number][] : 뭉쳐진 유니온을 다시 가변 길이 배열로 만듦
    8
    type AsArray = Input[number][];
    9
    // 결과: ("A" | "B" | "C")[] (길이 정보 사라짐)
    10
    11
    // 3. [K in keyof T] : 튜플 구조와 길이를 그대로 유지하며 순회
    12
    type AsMapped = { [K in keyof Input]: Input[K] };
    13
    // 결과: ["A", "B", "C"] (원래의 튜플 형태 유지)
  3. 조건부 타입에서 유니온의 분배(분배 조건부 타입)

    조건부 타입(T extends U ? X : Y)에 유니온 타입이 전달될 때, 제네릭 T가 아무런 가공도 되지 않은 순수한 상태(Naked Type Parameter)일 때만 유니온의 각 요소가 개별적으로 분배되어 평가된다. 그렇지 않은 경우 유니온이 통째로 평가된다.

    1
    type ToArray<T> = T extends any ? T[] : never;
    2
    type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
    3
    4
    // 1. 분배 발생: 순수한 타입 T를 그대로 사용
    5
    type Distributed = ToArray<string | number>;
    6
    // 결과: string[] | number[] (각각 쪼개서 처리됨)
    7
    8
    // 2. 분배 방지: T를 [T]처럼 인덱싱/튜플로 감싸는 별개의 처리를 추가
    9
    type NonDistributed = ToArrayNonDist<string | number>;
    10
    // 결과: (string | number)[] (통째로 평가됨)

풀이