ウェブサイト検索

TypeScript - ジェネリクスを使用する関数の返り値の型を取得する


免責事項:以下は過度に簡略化した関数です。役に立たないことは承知しています

function thinger<T>(thing: T): T {
    return thing;
}

const thing = thinger({ a: "lol" });

thing.a;

上記のコードは問題なくトランスパイルされます。しかし、thinger<T> の結果をオブジェクトに入れる必要があります。

interface ThingHolder {
    thing: ReturnType<typeof thinger>;
}

const myThingHolder: ThingHolder = {
    thing: thinger({ a: "lol" }),
};

しかし、型情報を失ってしまったので、myThingHolder.thing.a は動作しません

型 '{}' にプロパティ 'a' は存在しません

それで、次のことを試してみました

interface ThingHolder<T> {
    thing: ReturnType<typeof thinger<T>>;
}

const myThingHolder: ThingHolder<{ a: string }> = {
    thing: thinger({ a: "lol" }),
};

しかし、typeof thinger<T> は有効な TypeScript ではありません。

ジェネリクスに基づいて異なる戻り値の型を持つ関数の戻り値の型をどのように取得できますか?

回答 #1

これがあなたのニーズに合わないように見えるとしても、回答に入れておいたほうがいいかもしれません。TypeScriptには現在、ジェネリック値、高階型、任意の式に対するtypeofは存在しません。TypeScriptのジェネリックはその意味で「浅い」ものです。ですので、私の知る限りでは、残念ながら、型パラメータをジェネリック関数に差し込み、その結果をチェックする型関数を記述する方法はありません。

// doesn't work, don't try it
type GenericReturnType<F, T> = F extends (x: T) => (infer U) ? U : never

function thinger<T>(thing: T): T {
  return thing;
}

// just {}, 🙁
type ReturnThinger<T> = GenericReturnType<typeof thinger, T>;

だから私があなたにできることは、回避策を提案することだけです。最も明白な回避策は、thinger() が返すものをタイプエイリアスで記述し、それを複数の場所で使用することです。これはあなたが望むものの「逆」バージョンです。関数から返り値の型を抽出する代わりに、返り値の型から関数を作る方法です:

type ThingerReturn<T> = T; // or whatever complicated type you have

// use it here
declare function thinger<T>(thing: T): ThingerReturn<T>;

// and here
interface ThingHolder<T> {
  thing: ThingerReturn<T>;
}

// and then this works 🙂 
const myThingHolder: ThingHolder<{ a: string }> = {
  thing: thinger({ a: "lol" }),
};

それは役に立ちますか? あなたが望んでいたものではないことは分かっていますが、少なくとも前に進む可能性のある方法になればと思います。頑張ってください!

関連記事