◆結論

declare global {
  type AwaitType<T> =
    T extends Promise<infer U> ? U :
    T extends (...args: Array<any>) => Promise<infer V> ? V :
    T;
}

◆お気持ち

ReturnTypeっていうビルトインのtypeを使えば、「関数の戻り値の型」を引っ張ってこれますよね。

const hoge = (arg: number) => 'ちばけんま!';
type t = ReturnType<typeof hoge>;
// t のtypeは、hogeの戻り値の型であるstringになる。

それと同じノリで、関数の戻り値がPromiseであったらそれawaitした結果の型が欲しすぎない?喉から胃液が出るほど欲しい。

◆やりかた

以下を、適当な.tsとか.d.tsファイルに書いておく。あとはReturnTypeと同じように使えや豚が。

declare global {
  type AwaitType<T> =
    T extends Promise<infer U> ? U :
    T extends (...args: Array<any>) => Promise<infer V> ? V :
    T;
}

◆解説

declare globalで、globalにdeclare出来まする。詳しく説明すると色々あっけど、でも面倒だから端折る。globalにdeclare出来ますると認識しておけ。globalにdeclareしたくないならexport type AwaitType ~ みたいにしろや犬が。

「type AwaitType<T> = 」でAwaitTypeという、型パラメータを受け取る型を宣言しておる。AwaitTypeという名前が気に食わないなら自分で好きな名前つけろや鳥が。

「T extends Promise<infer U>」ってのは、「型パラメータTがPromiseの拡張であるか否か」という判定。つまり、TがPromise<U>みてぇなものだったらtrueが帰ってくる。例えばasyncな関数で「return hoge as U」とか書いた場合、返ってくるオブジェクトの型はPromise<U>。ただ、関数に対してAwaitTypeできればいいわけだからコレは無くてもいいっちゃいいし、要らないなら消せや猿が。

「? U :」ってのは、三項演算子と同じ意味のそれ。「T extends Promise<infer U>」がtrueだったらUを返すという意味。つまりAwaitTypeは型を受け取って型を返却する型、世に言うConditional Typesっていう特殊な型定義なんです実は。inferはinferenceの略で、推論という意味。inferという記述がなければ、「Uっていう型は定義されてないけども?え?アホなの?」と言われるんですね。だからinferをつけて「任意の型っすよ。とりあえずUという名前で扱っていこうや兄弟。」と教えてあげなければいけないんです羊が。

falseだった場合「? U :」の後ろに引き続いていく。「T extends (…args: Array<any>) => Promise<infer V> ? V」っていうのは、「何でもとにかく引数を受け取ってPromiseを返却してる関数である場合」を判定している。まず関数の型定義の書き方知っていれば読めるものではあるんだが、例えば「引数を受け取らず何も返さない関数の型」は「() => void」って表現される。あとは「...args」ってのは可変長引数の書き方がそうなってるからそういうこと。「Array<any>」は「any[]」でもいいから好みで好きに書き換えろや馬が。

前段の条件に当てはまらなかったときは、型パラメータとして渡ってきたTをそのまま返却してるに決まってんだろ蛇が。

◆その他ビルトインの型

Utility Typesのページを参照しとけや龍が。

◆結論

この兎が。いや、虎が!この牛が!鼠が!

「「「「 みんなそろって!!!!! 」」」」

\十二支戦隊 エトレンジャー!!!/
🐭🐮🐯🐰🐲🐍🐴🐏🐵🐔🐶🐗

~完~