はいさい!ちゅらデータぬオースティンやいびーん!
また、TypeScriptの便利なUtilityType
紹介をしたいと思います!
概要
Awaited
のUtilityTypeを使って、Promiseの決定後の型を推測する方法を紹介します。
背景
先ほど投稿したReturnType
についての記事を書きましたが、非同期処理のPromiseだったら、どうやってそのPromiseが履行された時の値を推測できるのかと気になった方がいらっしゃったかと思います。
実は、ReturnType
と同じように、TypeScriptにはもう一つ便利なUtilityType、Awaited
があります。
このUtilityTypeは比較的に新しく、TypeScript 4.5から使えるようになっています。
その使い方を説明してまいりましょう。
Awaited
の使い方
以下のようなコードを例に書いてみました。APIからTodoを取得して、{ id: number; title: string ... }
のように、数値もしくは文字列が入っているオブジェクトが返って来るはずです。
const getTodo = async (id: number)=> {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
if (!response.ok) throw Error(`Reponse failed: ${response.status}`);
const data = await response.json() as Record<string, string | number>;
return data;
}
URLは以下の無料Dummy APIのものです。遊びにとても便利なので、お勧めします。
前回の記事と同じように、このgetTodo
が返してくれる値を推測してみましょう。
type Todo = ReturnType<typeof getTodo>;
TypeScriptインタープリターにこの型定義の結果を聞いてみると、以下のような結果になります。
type Todo = Promise<Record<string, string | number>>
関数だから、ReturnTypeを使うとRecord<string, string | number>
が返って来るはずなのですが、今回は違うのですね。
なぜなら、async
の関数は、糖衣構文 であり、実際は.then().then()
の、ばりばりのPromiseだからなのです。
ここで、本当の、Promiseが履行した結果の戻り値がほしいので、以下のような型定義をします。
type Todo = Awaited<ReturnType<typeof getTodo>>;
さぁ、TypeScriptのインタープリターの結果は?
type Todo = {
[x: string]: string | number;
}
よろしい!
FileReaderの例
次、Fetch API以外の例も紹介してみます。
以下のようなFile
をBase 64の文字列に変換するPromiseを返す関数があります。
const convertFileToBase64 = (file: File) => new Promise<InstanceType<typeof FileReader>['result']>((resolve, reject) => {
const reader = new FileReader();
reader.addEventListener("error", () => reject(reader.error), { once: true });
reader.addEventListener("load", () => resolve(reader.result), { once: true });
reader.readAsDataURL(file);
});
これの戻り値の履行後の値を抽出すると、以下のようになります。
type ConvertedFile = Awaited<ReturnType<typeof convertFileToBase64>>; // type ConvertedFile = string | ArrayBuffer | null
JSDocで書く場合
JSDocでも同じような表記ができます!
/** @typedef {Awaited<ReturnType<typeof getTodo>>} Todo */
/** @param {number} id */
const getTodo = async (id)=> {
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
if (!response.ok) throw Error(`Reponse failed: ${response.status}`);
const data = /** @type {Record<string, string | number>} */ (await response.json());
return data;
};
まとめ
Awaited
の使い方を紹介してきましたが、いかがでしたでしょうか?
ReturnType
と一緒に使うと、どの関数でも戻り値の型を定義することができます。
JSDocsでもぜひ使ってみましょう!