1
0

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.

PromiseLike<T>型からT型を取り出すUtilityTypeを書いてみた

Last updated at Posted at 2019-10-17

TL;DR;

  • 色々試したものの、コメントでよりスマートな方法を教えていただいたのでそちらを参照された方がよいかと思います。

async Functionに対してReturnType的なことをやろうとしたものの、
PromiseLike型からT型を取り出すいい感じの方法が見つからなかったので、
勉強も兼ねて公式のUtilityTypeの実装を参考にしつつ書いてみた。

成果物

type Promised<T extends { then: (...args: any) => any }> = Parameters<Exclude<(T extends { then: (onfulfilled: infer P, ...args: any) => any } ? P : never), null | undefined>>[0];

//使用例
type t = Promised<Promise<string>>;
//t は string型

作る上でのメモ

genericsでPromiseLikeを受けられるようにする

PromiseLikeの定義は以下の通り。

interface PromiseLike<T> {
    /**
     * Attaches callbacks for the resolution and/or rejection of the Promise.
     * @param onfulfilled The callback to execute when the Promise is resolved.
     * @param onrejected The callback to execute when the Promise is rejected.
     * @returns A Promise for the completion of which ever callback is executed.
     */
    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): PromiseLike<TResult1 | TResult2>;
}

要するにthenというpropertyに何らかの関数があれば(T extends { then: (...args: any) => any })OK。
正確にはもっときちんと定義をしっかり付けるべきなのだろうけれども、実用上大きな問題ではないのでこの程度で妥協。

この定義からTを引っこ抜くためには、thenの第一引数(onfulfilled?)が存在する時の引数が一番早そうなので、その作戦で取りに行くことにする。

PromiseLike.thenから第一引数のonfulfilledの型を取ってくる

T extends { then: (onfulfilled: infer P, ...args: any) => any } ? P : never

ParametersやConstructorParametersを参考に書いた部分。
いわゆるConditionalTypeを使う。
infer P でonfulfilledについて型推論した結果をPに入れることが出来る。
(よってP=(value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null)

never部分は到達しないけれども書き方的に仕方ないので。

PからTを取り出す。

PからTを取り出すのは既存のUtilityTypeの組み合わせで十分

Excludeでundefinedとnullを抜く

Exclude<(T extends { then: (onfulfilled: infer P, ...args: any) => any } ? P : never), null | undefined>

Pは (value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null
要するに {欲しい関数} | undefined | nullなので、Union型からundefinedとnullを抜いてやればいい。
Union型から特定の要素を抜くのはExcludeで可能。

関数から引数の型を取得する

これで、 (value: T) => TResult1 | PromiseLike<TResult1>)が型として取れたので、
後は引数の型を取得すればOK。
Parametersを利用すれば関数の引数の型を配列で取得できる。([T]が返る)
第一引数が欲しいので添え字[0]でアクセスすれば目的のTが取得可能。

1
0
2

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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?