5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Promiseで返される型を取得する型定義

5
Last updated at Posted at 2019-12-09

概要

Jestでテストケースを書く際変更漏れを起こさないよう、実際のAPIクラスの型をimplementsする形で実装しようとしました。
ところが、Promiseで返すべき型を与えないとunknownと推論されてしまうので、元の定義を引っ張ってくる型定義を作成しました。

環境

Typescript 3.7.2で確認

起こったこと

下のようなメソッドを持つAPIクラスを作成し、exportしています。


interface GetResponse {
  id: number;
}

class OriginalApiClass {
  public get(): Promise<AxiosResponse<GetResponse>> {
    return null as any; // 実際のAPI処理
  }
}

export const OriginalApi = new OriginalApiClass();

このファイルに対し、下のようなモックを定義しようとしたところ、TSの型チェックに引っかかりました。


type Api = {
  [P in keyof typeof OriginalApi]: typeof OriginalApi[P];
}

// OriginalApiでpublicなメソッドをすべて定義させたいのでimplementsさせる
class Mock implements Api {
  // 型 'unknown' を型 'AxiosResponse<GetResponse>' に割り当てることはできません。ts(2416)
  public get() {
    return new Promise(resolve => {
      resolve({id: 1})
    });
  }
}

Promiseのジェネリクスパラメータに適切な型を渡してやれば解決する問題ですが、GetResponseをexportしてしまうと、他でサジェストに出てくるようになり邪魔なので、Promiseに渡すべき型を推論させる定義を作成しました。

作成した定義


export type PromiseParameter<T extends Record<string, any>, K extends keyof T> = (
  T[K] extends (...args: any) => infer A // 1
  ? (A extends Promise<any> // 2
    ? (
      A['then'] extends (resolve: (...args: infer B) => any, reject: any) => any // 3
      ? B[0] // 4
      : never
    )
    : never
  )
  : never
)

type Result = PromiseParameter<typeof OriginalApi, 'get'>; // => AxiosResponse<GetResponse>
  1. 最初に、メソッドの戻り型Ainfer Aで取得しています。
  2. 後続でPromiseとして扱えるように、AがPromiseのか判定します。
  3. Promiseだった場合、第1引数がresolve、第2引数がrejectなので、resolveの引数型Binfer Bで取得しています。
  4. (自分の場合)第1引数以外不要なので、B[0]でresolveの最初の引数の型を返しています。

これで、resolveで返されるべき型が取得できたので、あとはモックを以下のように書き直せば完成です。


class Mock implements Api {
  // エラーが出なくなり、resolveで本来入れるべきパラメータがサジェストされるようになります。
  public get() {
    return new Promise<PromiseParameter<typeof OriginalApi, 'get'>(reject => {
      resolve({
        config: {},
        data: {
          id: 1,
        },
        headers: {},
        status: 200,
        statusText: 'OK',
      }););
    });
  }
}

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?