TypeScript Awaited<T>ユーティリティ型の解説

Awaited<T>がPromise型を再帰的にアンラップする方法を学びます。非同期関数の戻り値型、ネストされたPromise、Promise.allの型付けの例を紹介します。

Union Types

詳細な説明

Awaitedの理解

Awaited<T>awaitPromise.then()の動作をモデル化します。ネストされたPromise型を再帰的にアンラップして最終的に解決される値の型を取得します。TypeScript 4.5で追加されました。

基本的な例

type A = Awaited<Promise<string>>;
// 結果: string

type B = Awaited<Promise<Promise<number>>>;
// 結果: number(再帰的にアンラップ)

type C = Awaited<string>;
// 結果: string(Promiseでない場合はそのまま通過)

非同期関数との使用

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

type User = Awaited<ReturnType<typeof fetchUser>>;
// 結果: { id: number; name: string }

Promise.allとの使用

type Results = Awaited<ReturnType<typeof Promise.all<
  [Promise<string>, Promise<number>, Promise<boolean>]
>>>;
// 結果: [string, number, boolean]

実践パターン: ジェネリック非同期ラッパー

async function withRetry<T extends Promise<unknown>>(
  fn: () => T,
  retries: number
): Promise<Awaited<T>> {
  for (let i = 0; i < retries; i++) {
    try {
      return await fn() as Awaited<T>;
    } catch (e) {
      if (i === retries - 1) throw e;
    }
  }
  throw new Error("Unreachable");
}

Awaited以前(4.5未満)

Awaitedが存在する前は、手動のアンラップを書く必要がありました:

type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;

ユースケース

Awaited<T>はPromiseの解決型が必要な場合に使用します。特に非同期関数のReturnTypeとの組み合わせ、ジェネリック非同期ユーティリティ、ネストされたPromiseを返すAPIの操作で有用です。

試してみる — TypeScript Utility Types

フルツールを開く