> Как типизировать функцию, работающую с массивом промисов с разными типами в TypeScript (TypeScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: IT-One

Стек: TypeScript

> Пример ответа

Короткий ответ

Для типизации функции с массивом промисов разных типов используйте дженерики с variadic tuple types: function all<T extends readonly unknown[]>(promises: {[K in keyof T]: Promise<T[K]>}): Promise<T>. Это позволяет сохранить точные типы каждого элемента. Альтернатива - Promise.all с mapped types или ручное указание перегрузок для ограниченного числа элементов.

Подробное объяснение

Основная задача - сохранить связь между типом каждого промиса в массиве и соответствующим значением в результате. TypeScript 4.0+ предоставляет variadic tuple types, которые идеально подходят для этой цели.

Механизм работы:

  • Дженерик T extends readonly unknown[] захватывает кортеж типов результата
  • Mapped type {[K in keyof T]: Promise<T[K]>} преобразует каждый элемент кортежа в промис того же типа
  • Возвращаемый тип - Promise<T> с сохранением структуры кортежа

Для функции, которая не просто вызывает Promise.all, а выполняет дополнительную логику (например, обработку ошибок, фильтрацию, трансформацию), типизация строится аналогично, с учётом конкретных преобразований.

На практике

Используйте встроенный Promise.all - он уже правильно типизирован для кортежей. Если пишете свою реализацию, применяйте variadic tuple types. Для динамических массивов (не кортежей) используйте Promise<T[]>, где T - объединение возможных типов.

Примеры сценариев:

  • Параллельная загрузка данных разных типов
  • Комбинирование результатов нескольких API-запросов
  • Обработка массива промисов с разными типами ошибок

Пример кода

TYPESCRIPT
// Базовая типизация с variadic tuple types
function allPromises<T extends readonly unknown[]>(
promises: { [K in keyof T]: Promise<T[K]> }
): Promise<T> {
return Promise.all(promises) as Promise<T>;
}
// Использование
const result = await allPromises([
Promise.resolve(42),
Promise.resolve("hello"),
Promise.resolve(true)
]);
// result: [number, string, boolean]
// Функция с трансформацией
async function transformAll<T extends readonly unknown[], R>(
promises: { [K in keyof T]: Promise<T[K]> },
transform: (values: T) => R
): Promise<R> {
const values = await Promise.all(promises) as T;
return transform(values);
}
// Для динамических массивов
async function processPromises<T>(
promises: Promise<T>[]
): Promise<T[]> {
return Promise.all(promises);
}

Как отвечать на собеседовании

Начните с ключевой идеи - variadic tuple types. Объясните, как mapped types сохраняют соответствие типов. Упомяните, что встроенный Promise.all уже корректно типизирован. Приведите пример с разными типами. Если спросят про ограничения - скажите, что для динамических массивов (неизвестное количество элементов) придётся использовать объединение типов.

Что проверяет интервьюер

  • Понимание продвинутых дженериков TypeScript
  • Знание variadic tuple types и mapped types
  • Умение типизировать асинхронные операции
  • Понимание разницы между кортежами и массивами в контексте типов
  • Способность объяснить, как TypeScript выводит типы для промисов

Типичные ошибки

  • Использование Promise<any[]> - теряет всю типовую информацию
  • Попытка типизировать через перегрузки для каждого количества элементов - негибко и громоздко
  • Забывают про readonly в дженерике для кортежей
  • Путают Promise<T[]> с кортежем промисов
  • Не учитывают, что Promise.all сам корректно выводит типы для кортежей

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью