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.
1const promise1 = Promise.resolve(3);2const promise2 = 42;3const promise3 = new Promise<string>((resolve, reject) => {4setTimeout(resolve, 100, 'foo');5});67// expected to be `Promise<[number, 42, string]>`8const p = PromiseAll([promise1, promise2, promise3] as const)910
선행 지식
-
Variadic Tuple
[...T]배열 리터럴을 튜플처럼 추론시키는 파라미터 패턴
1const a = [1, 2, 3]; // 배열 리터럴 number[]23// [...T] 형태의 파라미터 패턴을 사용하여 튜플로 추론하도록 유도4declare function f<T extends readonly unknown[]>(x: readonly [...T]): T;5const t = f([1, 2, 3]); // [number, number, number] (환경에 따라 튜플 유지)67// as const면 확정 튜플8const t2 = f([1, 2, 3] as const); // [1, 2, 3] -
TypeScript에서 배열/튜플 순회
T[number]는 “요소 타입”을 꺼내는 방식이라, 튜플에 쓰면 각 자리 정보가 사라지고 유니온으로 뭉개진다. 반대로 각 자리(0, 1, 2 …)를 유지하며 변환하려면 Mapped Type으로 keyof T를 순회한다.
1type Input = ["A", "B", "C"];23// 1. T[number] : 튜플을 깨고 요소들을 유니온으로 뭉침4type AsUnion = Input[number];5// 결과: "A" | "B" | "C"67// 2. T[number][] : 뭉쳐진 유니온을 다시 가변 길이 배열로 만듦8type AsArray = Input[number][];9// 결과: ("A" | "B" | "C")[] (길이 정보 사라짐)1011// 3. [K in keyof T] : 튜플 구조와 길이를 그대로 유지하며 순회12type AsMapped = { [K in keyof Input]: Input[K] };13// 결과: ["A", "B", "C"] (원래의 튜플 형태 유지) -
조건부 타입에서 유니온의 분배(분배 조건부 타입)
조건부 타입(
T extends U ? X : Y)에 유니온 타입이 전달될 때, 제네릭T가 아무런 가공도 되지 않은 순수한 상태(Naked Type Parameter)일 때만 유니온의 각 요소가 개별적으로 분배되어 평가된다. 그렇지 않은 경우 유니온이 통째로 평가된다.1type ToArray<T> = T extends any ? T[] : never;2type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;34// 1. 분배 발생: 순수한 타입 T를 그대로 사용5type Distributed = ToArray<string | number>;6// 결과: string[] | number[] (각각 쪼개서 처리됨)78// 2. 분배 방지: T를 [T]처럼 인덱싱/튜플로 감싸는 별개의 처리를 추가9type NonDistributed = ToArrayNonDist<string | number>;10// 결과: (string | number)[] (통째로 평가됨)