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が取得可能。