LoginSignup
5
1

More than 1 year has passed since last update.

TypeScriptでPromiseの決定後の型を抽出する方法

Posted at

はいさい!ちゅらデータぬオースティンやいびーん!

また、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でもぜひ使ってみましょう!

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1