前提知識
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で実装するべきか、明示的に型定義するべきかめっちゃ悩む