前提知識
const f = () => ({
fieldA: "hello",
fieldB: 12,
fieldC: null,
});
type FReturn = ReturnType<typeof f>;
この時FReturn
は次と同じになります。
type FReturn = {
fieldA: string
fieldB: number
fieldC: null
}
本題
次の場合にも同じようにFReturn
と型を取得したい時は?
const f = async () => ({
fieldA: "hello",
fieldB: 12,
fieldC: null,
});
type FReturn = /* 何かしらの型 */;
答え&解説
type FReturn = ReturnType<typeof f> extends Promise<infer T> ? T : never;
-
ReturnType<typeof f>
は前提の節で説明したようにfの返り値の型となります。つまり、async
を利用しているのでPromise<FReturn>
の型になります。 -
Type inference in conditional typesを利用します。
- Conditional typesでは三項子演算のように
T extends U ? X : Y
の形式で書きます。これは「TがUの型をextendしているならばX、そうでないならばY」という型を示します。 -
T extends U ? X : Y
のUの部分にinfer X
を利用することで、T extend U
が真となるようなX
の型を推論してくれるようになります。つまり上の例は、「ReturnType<typeof f>
がPromise<◯◯>
を実装するような◯◯が存在するならば、◯◯が型となり、それが存在しないならnever
」という型になります。ReturnType<typeof f>
はPromise<FReturn>
を実装しているので、上のコードが示す型はFReturn
となります。
- Conditional typesでは三項子演算のように
あるある言いたい
ReturnTypeで実装するべきか、明示的に型定義するべきかめっちゃ悩む