カテゴリ: TypeScript 更新日: 2026/03/10

TypeScriptのasync/awaitとPromise型推論を完全攻略!初心者がハマる原因と解決策

TypeScriptのasync/awaitで型がうまく推論されない原因と対処法
TypeScriptのasync/awaitで型がうまく推論されない原因と対処法

先生と生徒の会話形式で理解しよう

生徒

「TypeScriptで非同期処理を作っているのですが、asyncやawaitを使うとデータの種類がうまく判定されなくてエラーになってしまいます。」

先生

「それは型の推論がうまくいっていない証拠ですね。TypeScriptでは、あとから届くデータの中身をあらかじめ教えてあげる必要があるんですよ。」

生徒

「あとから届くデータ、ですか?具体的にどうやって型を指定すればいいのか教えてください!」

先生

「もちろんです!Promiseの仕組みから、型の書き方まで一つずつ丁寧に解説していきますね。」

1. 非同期処理とPromiseの基本を知ろう

1. 非同期処理とPromiseの基本を知ろう
1. 非同期処理とPromiseの基本を知ろう

プログラミングの世界には、非同期処理という考え方があります。これは、時間がかかる作業を待っている間に、他の作業を先に進めておく仕組みのことです。例えば、レストランで料理を注文したあと、料理ができあがるまで席でスマホを見たり会話をしたりして待つのと同じです。料理が完成するのを何もせずにじっと厨房の前で立ち尽くして待つ必要はありませんよね。

この「あとで料理が届くよ」という約束の状態を、TypeScriptやJavaScriptではPromise(プロミス)と呼びます。Promiseは直訳すると「約束」です。今はまだ手元にデータはないけれど、未来のいつか、成功してデータが返ってくるか、あるいは失敗してエラーが返ってくることを約束するオブジェクトなのです。

TypeScriptはこのPromiseを扱うのが得意ですが、初心者の方は「中身が何であるか」を正しく設定しないと、プログラムが混乱してしまいます。まずはこの約束の概念をしっかり理解することが、型推論をマスターする第一歩となります。

2. asyncとawaitの役割と書き方

2. asyncとawaitの役割と書き方
2. asyncとawaitの役割と書き方

非同期処理をより人間が読みやすい形で書くために登場したのが、async(エイシンク)とawait(アウェイト)です。これらはセットで使われることが多い魔法の言葉です。関数の前にasyncをつけると、その関数は自動的にPromiseを返すようになります。そして、その関数の中でawaitを使うと、「データが返ってくるまでここで少し待ってね」という指示になります。

通常、非同期処理は非常に複雑な書き方になりがちですが、これらを使うことで、上から下へ順番に処理が進んでいるかのようにコードを書くことができます。しかし、ここで問題になるのが型推論です。型推論とは、TypeScriptが「この変数は数字だね」「これは文字列だね」と自動で判断してくれる機能のことですが、非同期処理の場合はこの判断が難しくなることがあるのです。

3. 型がうまく推論されない主な原因

3. 型がうまく推論されない主な原因
3. 型がうまく推論されない主な原因

なぜTypeScriptは、async関数で型をうまく予測できないことがあるのでしょうか。最大の原因は、Promiseの中身を定義していないことにあります。例えば、箱の中に何かが入っていることはわかっても、開けてみるまでリンゴなのかミカンなのかわからない状態と同じです。TypeScriptは非常に慎重な性格なので、中身がわからないものに対しては「これは何かわかりません(unknownやany)」というラベルを貼ってしまいます。

また、外部のサーバーからデータを取得するAPI通信などでは、どのような形式のデータが返ってくるかをTypeScriptが事前に知る術がありません。そのため、開発者が明示的に「こういう形のデータが返ってくるはずだよ」と教えない限り、推論は途切れてしまうのです。これが原因で、せっかくTypeScriptを使っているのに、コードを書いている途中で便利な入力補完が効かなかったり、エラーが表示されたりすることになります。

4. Promiseに型を指定する具体的な方法

4. Promiseに型を指定する具体的な方法
4. Promiseに型を指定する具体的な方法

型を正しく推論させるための最も確実な方法は、関数の戻り値に対してジェネリクスを使うことです。ジェネリクスとは、型をパラメータのように扱える仕組みで、Promise<型名>という形式で記述します。これにより、「この約束が果たされたときには、この型のデータが返ってきます」という宣言になります。

それでは、実際にシンプルなコードで確認してみましょう。名前を返すだけの簡単な非同期関数の例です。


async function getUserName(): Promise<string> {
    return "たろう";
}

async function main() {
    const name = await getUserName();
    console.log(name.length);
}

このコードでは、Promise<string>と書くことで、awaitした結果が必ず文字列(string)になることを伝えています。そのおかげで、変数nameに対して文字列のプロパティであるlength(文字数)を安全に使うことができるようになるのです。もし型を指定しなかったら、TypeScriptはnameが何かわからず、エラーを出していたかもしれません。

5. オブジェクトを扱う高度な型推論

5. オブジェクトを扱う高度な型推論
5. オブジェクトを扱う高度な型推論

実際の開発では、単なる文字列だけでなく、複数のデータが集まったオブジェクトを扱うことが多いでしょう。例えば、ユーザーのID、名前、年齢がセットになったデータなどです。このような場合は、まずinterface(インターフェース)やtype(タイプ)を使って、データの設計図を作成します。設計図を先に作っておくことで、Promiseの中身を詳細に定義できるようになります。


interface User {
    id: number;
    name: string;
    isPremium: boolean;
}

async function fetchUser(): Promise<User> {
    // 実際はここでインターネットからデータを取得するイメージです
    return {
        id: 1,
        name: "山田はなこ",
        isPremium: true
    };
}

async function displayUser() {
    const user = await fetchUser();
    console.log("ユーザー名: " + user.name);
}

このように、自作したUserという型をPromise<User>として指定することで、user.nameuser.idといった項目が正しく認識されます。パソコンのキーボードでuser.と打ち込んだ瞬間に、候補としてnameidが表示されるようになるのは、この型指定のおかげです。これにより、打ち間違いによるミスを劇的に減らすことができます。

6. 配列データの非同期取得と型定義

6. 配列データの非同期取得と型定義
6. 配列データの非同期取得と型定義

次に、複数のデータが並んだ配列を取得する場合の対処法を見ていきましょう。ニュース記事のリストや、商品の在庫一覧などを取得する際に使います。配列の場合は、Promise<型名[]>という書き方をします。末尾にブラケット(角括弧)をつけるのがポイントです。


type Product = {
    title: string;
    price: number;
};

async function getProducts(): Promise<Product[]> {
    const items = [
        { title: "ノートパソコン", price: 120000 },
        { title: "マウス", price: 30000 }
    ];
    return items;
}

async function showTotal() {
    const products = await getProducts();
    products.forEach(p => {
        console.log(p.title + "の価格は" + p.price + "円です");
    });
}

実行結果は以下のようになります。


ノートパソコンの価格は120000円です
マウスの価格は30000円です

配列の型推論がうまくいっていると、forEachなどの便利な命令を使っている際にも、中身の一つひとつの要素がProduct型であることをTypeScriptが理解してくれます。プログラミング初心者がよくやってしまう「配列なのに単体のデータとして扱ってしまう」といったミスを防ぐ強力な盾になってくれます。

7. 型アサーションを使った最終手段

7. 型アサーションを使った最終手段
7. 型アサーションを使った最終手段

どうしてもTypeScriptの推論がうまくいかない場合や、外部ライブラリの都合で型が定義されていない場合には、型アサーションという機能を使うことがあります。これは「このデータは絶対にこの型だから信じて!」とプログラマがTypeScriptに強制的に指示を出す方法です。asというキーワードを使います。


async function getUnknownData() {
    const response = await fetch("https://example.com/api/data");
    const data = await response.json();
    
    // 取得したデータが何かわからないので、無理やり型を教える
    const fixedData = data as { message: string };
    console.log(fixedData.message);
}

ただし、型アサーションは非常に強力ですが、使いすぎには注意が必要です。実際にはデータの中身が違うのに、無理やり違う型だと嘘をついてしまうと、プログラムが実行中に壊れてしまう原因になるからです。可能な限り、これまでに紹介したPromise<T>の形式で正しく定義することを優先し、どうしても解決できない時の予備手段として覚えておきましょう。初心者のうちは、まずは正攻法である戻り値への型指定を練習することをおすすめします。

8. エラーハンドリングと型の安全性

8. エラーハンドリングと型の安全性
8. エラーハンドリングと型の安全性

非同期処理では、常に成功するとは限りません。インターネットが切れていたり、サーバーが故障していたりすることもあります。そのために必要なのがtry-catch(トライ・キャッチ)という仕組みです。TypeScriptでは、エラーが発生した際の「エラーオブジェクト」の型についても考える必要があります。

最新のTypeScriptでは、キャッチされたエラーの型はデフォルトでunknown(不明)となっています。これは安全性を高めるための仕様です。エラーが発生した時に、そのエラーがどのようなメッセージを持っているかを安全に確認するためには、型のチェックが必要になります。これにより、予期せぬエラーで画面が真っ白になってしまうような事態を未然に防ぎ、ユーザーにとって優しいアプリを作ることができるようになります。型を意識することは、単にプログラムを書くのを楽にするだけでなく、最終的に使う人の安心にも繋がっているのです。

カテゴリの一覧へ
新着記事
New1
JavaScript
JavaScriptの配列のコピー方法(スプレッド構文・slice)
New2
TypeScript
TypeScriptの関数に型をつける方法(引数・戻り値)を初心者向けに徹底解説!
New3
TypeScript
TypeScriptのasync/awaitとPromise型推論を完全攻略!初心者がハマる原因と解決策
New4
TypeScript
TypeScriptの始め方:開発環境の構築手順【初心者向け】
人気記事
No.1
Java&Spring記事人気No1
JavaScript
JavaScriptのインストール方法まとめ!Windows・Mac・Linux別にステップ解説
No.2
Java&Spring記事人気No2
JavaScript
JavaScriptプログラムの実行方法まとめ!ブラウザ・Node.js・コンソールの使い方
No.3
Java&Spring記事人気No3
JavaScript
JavaScriptのDOM操作におけるブラウザ互換性ポイントをやさしく解説
No.4
Java&Spring記事人気No4
JavaScript
JavaScriptのtoStringとString関数の違いを初心者向けに解説
No.5
Java&Spring記事人気No5
JavaScript
JavaScriptで文字列をforループで1文字ずつ処理する方法!初心者向け解説
No.6
Java&Spring記事人気No6
TypeScript
TypeScriptでコメントを書く正しい書き方と使い分け【初心者向けにやさしく解説】
No.7
Java&Spring記事人気No7
JavaScript
JavaScriptで要素の高さ・幅を取得する方法を完全解説!offsetHeightなどDOM操作入門
No.8
Java&Spring記事人気No8
JavaScript
JavaScriptのonclick・onchangeなどの基本イベントを理解しよう