フロントエンド開発の現場を支える戦友のみなさん、お疲れ様です。チーフアーキテクトの「ナオト」です。
モダンなフロントエンド開発において、私たちは日々、非同期処理(Promise)と格闘していますよね。APIからのデータ取得、ダイナミックインポート、Web Workerとの通信……。JavaScriptがシングルスレッドで軽快に動く裏には、常にPromiseの存在があります。
しかし、TypeScriptで開発していると、こんな壁にぶつかったことはありませんか?
「この非同期関数が最終的に返す『中身の型』だけをスマートに抽出して、別のコンポーネントや関数に引き渡したいのに、型定義が `Promise
かつては `Promise` のジェネリックパラメータを手動でこねくり回したり、最悪の場合 `any` に逃げてお茶を濁したりする現場を何度も目にしてきました。
そんな不毛な消耗戦に終止符を打ったのが、TypeScript 4.5で導入された救世主、`Awaited
今回は、この `Awaited
—
そもそも `Awaited` とは何なのか?
一言で言えば、`Awaited
「単に `Promise
なぜ単純な「Unwrap」ではダメなのか?
例えば、Promiseが何重にもネストされていたり(`Promise
`Awaited
// 1. 基本的なPromiseの解決
type Simple = Awaited
// => string
// 2. ネストされたPromiseの解決(自動的に一番奥まで剥いてくれる!)
type Nested = Awaited
// => number
// 3. Promiseではない「生の値」を渡した場合(そのまま通してくれる頑丈さ)
type RawValue = Awaited
// => boolean
この「どんな状態の型が来ても、最終的に await されたときの型に収束させる」という堅牢さこそが、`Awaited
—
ブラウザの裏側(ランタイム)とTypeScriptの静的型世界のリンク
ここで少し、ブラウザやNode.jsの「裏側」に目を向けてみましょう。アーキテクトとして、言語仕様だけでなくランタイムの挙動と型システムを脳内でシンクロさせることは極めて重要です。
JavaScriptの仕様(ECMAScript)において、`async/await` は単なるシンタックスシュガー(構文糖衣)です。裏側では、ジェネレータとPromiseのチェイン、そしてマイクロタスクキュー(Microtask Queue)が激しく動いています。
// ブラウザのランタイムが処理するイメージ
async function fetchData() {
const response = await fetch(‘/api/user’);
const data = await response.json();
return data;
}
ブラウザが `await` キーワードに到達すると、実行コンテキストは一度一時停止し、Promiseの解決を待つためにマイクロタスクキューにタスクを登録します。そして解決されると、イベントループによって実行が再開され、ラップが解かれた「生の値」が変数に代入されます。
TypeScriptの `Awaited
型定義のレベルで `Awaited
—
実務で即戦力になる実践コード・レシピ
それでは、現場のコードベースに今すぐ導入できる、具体的かつ実用的なサンプルコードを見ていきましょう。そのままプロジェクトにコピペして使っていただいて構いません。
レシピ1:APIクライアントのレスポンス型を自動抽出する
外部APIを叩くSDKや、自前のAPIクライアント関数があるとします。その関数が返す「データの型」だけを抽出して、フロントエンドのReactやVueのコンポーネントのProps型として再利用したいケースです。
// 架空のAPIクライアント関数
// 実際の実務では、サードパーティ製ライブラリ(Axiosなど)や自動生成されたAPIコードであることが多いです
export const getMyUserProfile = async (userId: string) => {
// 擬似的なAPI遅延処理
await new Promise((resolve) => setTimeout(resolve, 500));
return {
id: userId,
nickname: “TypeScript極めし者”,
roles: [“admin”, “developer”] as const,
updatedAt: new Date(),
};
};
// —————————————————-
// ここからが Awaited
// —————————————————-
// 1. まず、関数の「戻り値の型」を取得する(この時点では Promise<...> に包まれている)
type GetUserProfileReturnType = ReturnType
// => Promise<{ id: string; nickname: string; roles: readonly ["admin", "developer"]; updatedAt: Date; }>
// 2. Awaited
export type UserProfile = Awaited
/
UserProfile の実際の型:
{
id: string;
nickname: string;
roles: readonly [“admin”, “developer”];
updatedAt: Date;
}
/
// 3. 抽出した型をコンポーネントのPropsなどで安全に再利用する
export const renderProfileCard = (profile: UserProfile) => {
console.log(`ユーザー名: ${profile.nickname}`);
};
解説:なぜ `ReturnType` だけではダメなのか?
`ReturnType
これをコンポーネントのPropsに渡そうとすると、「Promiseオブジェクトを渡さなきゃいけないの?」とコンパイラに怒られてしまいます。`Awaited` を組み合わせることで初めて、コンポーネントに渡すべき「純粋なデータ構造の型」が手に入るのです。
—
レシピ2:ジェネリクスと組み合わせて「汎用非同期ラッパー」を型安全にする
実務では、非同期処理にローディングやログ出力を挟む「高階関数(Wrapper)」を作ることがよくあります。
どんな非同期関数が来ても、その解決後の型を完璧に追跡できる、極めて型安全なラッパー関数を定義してみましょう。
/
- 任意の非同期関数を実行し、実行時間(ms)と解決された結果を返す汎用ラッパー
/
export async function measureAsyncPerformance
asyncFn: T,
…args: Parameters
): Promise<{ duration: number; result: Awaited
const startTime = performance.now();
// 関数の実行(awaitで解決する)
const result = await asyncFn(…args);
const endTime = performance.now();
const duration = endTime – startTime;
// 戻り値の result は Awaited
return {
duration,
result,
};
}
// — 使用例 —
const fetchHeavyData = async (limit: number) => {
return {
items: Array.from({ length: limit }, (_, i) => `Item-${i}`),
fetchedAt: Date.now()
};
};
async function run() {
// measureAsyncPerformance の型推論により、
// result の中身が { items: string[]; fetchedAt: number; } であることをエディタが完全検知する!
const { duration, result } = await measureAsyncPerformance(fetchHeavyData, 5);
console.log(`処理時間: ${duration}ms`);
console.log(`取得データ件数: ${result.items.length}件`); // 型補完が爆速で効く!
}
このラッパー関数の素晴らしいところは、引数に渡された関数 `asyncFn` の引数の型(`Parameters
—
現場でやりがちなアンチパターンと回避策
アンチパターン:`Promise` や `Promise` からの強引なキャスト
型定義が面倒だからと、APIのレスポンス定義を `Promise
// ❌ 悪い例: 呼び出し側で無理やりキャストしている
const rawData = (await badApiCall()) as MyDataType;
回避策:
API定義側で正しい戻り値をアノテーションできない場合でも、`Awaited` とヘルパー関数を組み合わせて、境界部分で型を縛りましょう。
また、`Awaited
type MaybePromise = Promise
// TypeScriptは賢いので、null や undefined を維持したまま Promise の中身だけを剥いてくれる
type Resolved = Awaited
// => string | null | undefined
このように、ランタイムで発生し得る「値がまだ無い状態(nullish)」に対しても頑丈に振る舞うため、無理なキャストをせずとも自然なオプショナルチェイニング等で処理を記述できます。
—
終わりに:型を制する者が、フロントエンドの複雑性を制する
かつて、Promiseの型解決は `T extends Promise
しかし、現在は `Awaited
非同期処理が入り乱れるモダンフロントエンドにおいて、「実行時(Runtime)の値を、いかに静的(Static)な型世界へシームレスにマッピングするか」は、アプリケーションの堅牢性を担保するための最重要課題です。
今回紹介した `Awaited
「型安全であること」は、開発スピードを落とす足枷ではなく、「恐れることなく高速にリリースするための最強の盾」です。
また現場で役立つ実践的な知見を共有しますね。コードの海を、共にハッピーに泳ぎ切りましょう!

コメント