TypeScriptでasync関数とawaitの型を明示的に書く方法を徹底解説!初心者でもわかる非同期処理の基本
生徒
「先生、TypeScriptでasyncとかawaitってよく見かけるんですけど、どういう意味なんですか?」
先生
「いい質問ですね。これはプログラムの中で『非同期処理』と呼ばれる仕組みを扱うときに使うキーワードなんです。簡単に言えば、『時間がかかる処理を待ちながらプログラムを書くための道具』なんですよ。」
生徒
「なるほど!でも、型はどうやって書けばいいんですか?」
先生
「それでは、async関数とawaitの型を明示的に書く方法を一緒に学んでいきましょう!」
1. 非同期処理(ひどうきしょり)とは?
まず最初に「非同期処理」という言葉を理解しましょう。非同期処理とは、プログラムの中で時間がかかる処理を待っている間に、他の処理を進められる仕組みです。例えば、インターネットからデータを取得する処理や、ファイルを読み込む処理はすぐに終わらないことがあります。そんなときに便利なのが、async(アシンク)関数とawait(アウェイト)です。
例えるなら「ご飯を炊いている間に掃除をする」ようなイメージです。炊飯器がご飯を炊いてくれる間に、あなたは他の作業を進められます。そして、ご飯が炊き上がったらその結果を受け取る、これが非同期処理の考え方です。
2. async関数とは?
asyncを付けた関数は、必ずPromiseという特別な「結果の箱」を返します。Promise(プロミス)とは、「まだ結果は出ていないけれど、将来この中に結果が入るよ」という約束の箱のようなものです。
TypeScriptでは、このPromiseの中に「どんな型の結果が入るのか」を明示的に書くことができます。これによって、コードを読む人にもコンピュータにも「この関数の戻り値は何なのか」がわかりやすくなります。
async function fetchNumber(): Promise<number> {
return 42;
}
この例では、関数fetchNumberはPromise<number>を返すと明示されています。つまり「将来、数字(number型)が返ってくるよ」と伝えているわけです。
3. awaitの使い方と型
awaitは「Promiseの中の結果が出るまで待つ」という命令です。awaitを使うと、Promiseの中に入っている値を取り出せます。
例えば、先ほどの関数を使ってみましょう。
async function main(): Promise<void> {
const result: number = await fetchNumber();
console.log(result);
}
このコードでは、await fetchNumber()によって、Promise<number>から実際のnumber型の値が取り出されます。そのため、変数resultはnumber型として書けるわけです。
4. async関数と戻り値の型を明示するメリット
プログラミングを始めたばかりだと「型を明示するのは面倒そう」と思うかもしれません。でも、実際にはとても役立ちます。例えば、型を明示することで以下のメリットがあります。
- どんな値が返ってくるのか、他の人がすぐに理解できる
- コンピュータが型チェックをしてくれるので、間違いを早く見つけられる
- コードが長くなったときにも安心して修正できる
つまり、型を明示することは「プログラムの安全ベルト」のようなものです。
5. 実際の例:データ取得処理
もう少し現実的な例を見てみましょう。例えば、API(インターネット上のサービスからデータを取ってくる仕組み)を使う場合です。
type User = {
id: number;
name: string;
};
async function fetchUser(): Promise<User> {
return {
id: 1,
name: "太郎"
};
}
async function showUser(): Promise<void> {
const user: User = await fetchUser();
console.log(user.name);
}
ここでは、fetchUser関数の戻り値がPromise<User>であると明示されています。そして、awaitで取り出した値はUser型として扱えます。これにより、user.nameのように型の安全を保ちながらコードを書けます。
6. void型とasync関数
戻り値を使わない場合には、Promise<void>を指定します。voidとは「何も返さない」という意味です。
async function logMessage(): Promise<void> {
console.log("処理が完了しました");
}
この関数は結果を返さず、ただメッセージを表示するだけです。こういう場合でも、asyncを付けると必ずPromiseを返すため、型をPromise<void>と明示しておくと安心です。
まとめ
非同期処理を扱ううえで、async関数とawaitの使い方、そしてそれに付随する戻り値の型指定はとても重要な要素になります。ここまで学んだ内容を振り返りながら、実際にどのような場面で型を明示すると便利なのか、またその意味を丁寧に整理していきます。非同期処理は現代のアプリケーション開発において欠かせないしくみであり、特にデータ取得やファイル操作、API連携など、実践的な開発では頻繁に使われる領域です。そのため、型をしっかり記述しておくことで、不具合を未然に防ぎ、読みやすく安全なコードを書けるようになります。
まず大切なのは、asyncを付けた関数が必ずPromiseを返すという点でした。これはTypeScriptが持つ型の仕組みと非常に相性が良く、戻り値の型を明確に示すことで「この戻り値が将来どんな型になるのか」をはっきりと表現できます。型を明示していないと、後から関数を使う側が戸惑ったり、誤った使い方をしてエラーに繋がることがあります。型を定義するという行為は、一種の「将来の自分や他の開発者へのメッセージ」とも言えるのです。
また、awaitを使うことでPromiseの中の値を取り出すことができ、取り出した結果に正しい型を与えることができる点も非常に便利です。たとえば、APIから取得したユーザー情報をUser型として扱う場合、戻り値をPromise<User>と明示しておくことで、取り出した値を確実にUserとして扱えるようになります。これにより、値の取り違いやプロパティ名の打ち間違いを早い段階で防ぐことができます。
もうひとつ見逃してはいけないポイントは、void型の扱いです。非同期関数であっても、何も返さない場合がもちろんあります。単にログを表示したり、画面の状態を変えるだけの処理などが代表例です。その場合でもPromise<void>と明示しておくことで、関数がどのような目的を持っているかが明確になり、コードを読む側にとって理解しやすくなります。「この関数は結果を返さないんだな」という情報が型から自然に伝わってくるのは、静的型付け言語であるTypeScriptの大きな利点です。
実際のプロジェクトでは、複数の非同期処理が連鎖したり、複雑なデータの受け渡しが行われることも多くなります。その場合、型を正しく定義しておくことで、プロジェクト全体の流れを把握しやすくなり、保守性も大きく向上します。特に数ヶ月後の自分が読み返したときや、別の開発者が引き継ぐ場面では、型が明確に書かれているコードが大きな助けになります。「何が返ってくるのか」「どんなデータを使いたいのか」を正確に表せるという点で、型の明示は非常に強力な技術だといえます。
以下に、今回の内容をふまえたサンプルコードをもう一度整理しておきます。async関数、await、戻り値の型指定がどのように組み合わされるのか、ぜひ見直してみてください。
サンプルコード:async関数と戻り値の型を組み合わせた例
type Article = {
id: number;
title: string;
author: string;
};
async function fetchArticle(): Promise<Article> {
return {
id: 10,
title: "非同期処理の理解を深める記事",
author: "山田"
};
}
async function displayArticle(): Promise<void> {
const article: Article = await fetchArticle();
console.log("記事タイトル:", article.title);
console.log("著者:", article.author);
}
ここでは、APIから取得すると仮定した記事情報をArticle型で定義し、その型をPromiseに乗せて扱う例を示しました。awaitを使って取得した値は、明確にArticleとして扱えるため、プロパティへのアクセスも安全です。こうした型の一貫した使い方は、アプリケーション全体の品質向上にもつながり、安心して機能を追加したり修正できる土台になります。
最後に、asyncとawaitの基本を押さえたうえで型を明示しながら非同期処理を書くことは、TypeScriptを使いこなすための大きな一歩です。今後さらに高度な非同期処理やAPI連携を学ぶ際にも、この基礎が必ず役に立ちます。ぜひこの考え方を実際のコードで活かしてみてください。
生徒
「今日の内容を通して、asyncとawaitの型を明示するとこんなに分かりやすくなるんだと感じました。特にPromiseの中にどんな値が入るのか明確になるのが便利ですね。」
先生
「そうですね。非同期処理は便利ですが、構造が複雑になりやすいので、型をきちんと決めておくことが理解の助けになります。安全に開発を進める意味でもとても大切です。」
生徒
「User型やArticle型みたいに、自分で型を作ってPromiseの中に入れるのも便利だと思いました。実際のアプリでよく使いそうですね。」
先生
「ええ、実際の開発では自分で定義した型をやり取りする場面がとても多いですよ。型があればコードの見通しも良くなって、間違いが減ります。」
生徒
「今回の学びを忘れないように、async関数を書くときは常に型も意識して練習してみます!」