Promise.all
Завдання
Типізувати функцію PromiseAll
, яка приймає масив PromiseLike
об’єктів і
повертає Promise<T>
, де T
, це масив типів результату виконання Promise
.
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
setTimeout(resolve, 100, "foo");
});
// expected to be `Promise<[number, number, string]>`
const p = Promise.all([promise1, promise2, promise3] as const);
Розв’язок
Почнемо з простого – функція що повертає Promise<T>
.
declare function PromiseAll<T>(values: T): Promise<T>;
Тепер треба придумати, як вирахувати типи з виконаних Promise
. Почнемо з
факту, що values
це масив. Виразимо це в наших типах.
Використовуючи
варіативні типи
вказуємо, що values
це масив, а T
елементи цього масиву:
declare function PromiseAll<T extends unknown[]>(values: [...T]): Promise<T>;
Отримуємо помилку “Argument of type ‘readonly [1, 2, 3]’ is not assignable to
parameter of type ‘[1, 2, 3]’.“. Тому що values
не очікує модифікатор
readonly
в параметрі. Виправимо це, додавши модифікатор до параметра функції:
declare function PromiseAll<T extends unknown[]>(
values: readonly [...T],
): Promise<T>;
В нас є рішення, яке проходить один з тестів. Це тому, що цей тест не містить
Promise
. Ми повертаємо такий самий масив, який ми отримали в values
. Але як
тільки ми отримуємо Promise
, як елемент values
, наше рішення перестає
працювати.
Це тому, що ми не розгорнули Promise
, а повернули його. Замінимо тип T
на
умовний тип, який буде перевіряти, чи є елемент Promise
. Якщо елемент це
Promise
то повертаємо внутрішній тип, інакше – тип без змін.
declare function PromiseAll<T extends unknown[]>(
values: readonly [...T],
): Promise<T extends Promise<infer R> ? R : T>;
Рішення досі помилкове, тому що T
, не об’єднання, а кортеж. Тож, потрібно
проітерувати всі елементи кортежу і перевірити, є поточний елемент Promise
чи
ні.
declare function PromiseAll<T extends unknown[]>(
values: readonly [...T],
): Promise<{ [P in keyof T]: T[P] extends Promise<infer R> ? R : T[P] }>;
Коментарі