Awaited: тип для промисов

Awaited<T> — утилитарный тип TypeScript, который рекурсивно разворачивает тип промиса: Awaited<Promise<string>> даёт string, Awaited<Promise<Promise<number>>> даёт number.

Зачем нужно

До появления Awaited (TypeScript 4.5) извлечь тип разрешённого значения промиса было громоздко — нужно было писать ReturnType<typeof fn> с ручным раскрытием. Awaited решает задачу рекурсивно и корректно обрабатывает thenable-объекты, что критично при типизации async/await-кода.

Где используется

  • Типизация результатов Promise.all, Promise.allSettled, Promise.race
  • Утилитарные функции, принимающие или возвращающие промисы
  • Вычисление типа возвращаемого значения async-функции через ReturnType
  • Generic-обёртки над fetch/API-вызовами

Основной контент

Базовое использование

type A = Awaited<Promise<string>>;         // string
type B = Awaited<Promise<Promise<number>>>; // number
type C = Awaited<string>;                  // string (не промис — возвращается как есть)
type D = Awaited<Promise<string | null>>;  // string | null

С ReturnType async-функций

async function fetchUser(id: number) {
  const res = await fetch(`/api/users/${id}`);
  return res.json() as Promise<{ id: number; name: string }>;
}

// Без Awaited пришлось бы писать сложнее
type User = Awaited<ReturnType<typeof fetchUser>>;
// { id: number; name: string }

function processUser(user: User) {
  console.log(user.name);
}

Promise.all и Awaited

const results = await Promise.all([
  fetch("/api/users").then((r) => r.json() as Promise<User>),
  fetch("/api/posts").then((r) => r.json() as Promise<Post>),
]);

// results: [User, Post]
// TypeScript автоматически применяет Awaited к каждому элементу кортежа
type Results = Awaited<typeof results>; // [User, Post]

Пользовательский generic с Awaited

// Функция, которая принимает промис или значение и всегда возвращает Promise
async function wrap<T>(value: T | Promise<T>): Promise<Awaited<T>> {
  return value as Awaited<T>;
}

const a = await wrap(42);          // number
const b = await wrap(Promise.resolve("hello")); // string

Реализация Awaited внутри TS

// Упрощённое определение из стандартной библиотеки
type Awaited<T> =
  T extends null | undefined
    ? T
    : T extends object & { then(onfulfilled: infer F, ...args: any): any }
      ? F extends (value: infer V, ...args: any) => any
        ? Awaited<V>
        : never
      : T;

Частые ошибки

  • Использовать Promise<ReturnType<T>> вместо Awaited<ReturnType<T>> — не раскрывает вложенные промисы.
  • Забывать что Awaited<string> = string — тип не требует промиса, он просто «разворачивает» если промис есть.
  • Путать с Promise<T>Awaited извлекает тип из промиса, а не создаёт промис.

Связанные темы

Ресурсы