関数の戻り値の型を取得する


101

私は次の機能を持っています:

function test(): number {
    return 42;
}

関数のタイプは、次を使用して取得できますtypeof

type t = typeof test;

ここでtは、になります() => number

関数の戻り値の型を取得する方法はありますか?の代わりtになりたいnumberです() => number


2
いいえ、とにかくtypeofではありません。typeofは「function」のみを返します(私のケーシングが間違っている可能性があります)。
Igor

これが可能になれば素晴らしいと思います。関数から型をプッシュするときに型を定義することがありますが、この構造をキャプチャする(良い)方法はありません。
ヨルゲンTvedt

回答:


164

編集

TypeScript 2.8以降、これは公式に可能ReturnType<T>です。

type T10 = ReturnType<() => string>;  // string
type T11 = ReturnType<(s: string) => void>;  // void
type T12 = ReturnType<(<T>() => T)>;  // {}
type T13 = ReturnType<(<T extends U, U extends number[]>() => T)>;  // number[]

詳細はこちらをご覧ください。

TypeScriptは素晴らしいです!


昔ながらのハック

残念ながら、ライアンの答えはもう機能しません。しかし、私はそれを私が不当に満足しているハックで修正しました。見よ:

const fnReturnType = (false as true) && fn();

これは、falseをリテラル値trueにキャストすることで機能するため、型システムは戻り値が関数の型であると見なしますが、実際にコードを実行すると、falseで短絡します。

TypeScriptが最適です。:D


53
イエス様、それは恐ろしいことです。
ジョンワイズ2017年

フローの場合:const DUMMY =((null:any):Object)&& fn()export type Type = typeof DUMMY
David Xu

2.8をインストールして試してみると、次のエラーが発生する理由がわかり Cannot find name 'ReturnType'ます。vscodeの使用
NSjonas

1
@NSjonasは、他のバージョンを使用するようにvscodeに指示する必要があります。右下のステータスバーだと思います。
Meirion Hughes 2018

12
私はしなければなりませんでしたReturnType<typeof fn>(答えReturnType<fn>は十分であることを意味します)。
spacek33z

49

TypeScript 2.8で最も簡単な方法:

const foo = (): FooReturnType => {
}

type returnType = ReturnType<typeof foo>;
// returnType = FooReturnType

5

以下のコードは、関数を実行しなくても機能します。これはreact-redux-typescriptライブラリ(https://github.com/alexzywiak/react-redux-typescript/blob/master/utils/redux/typeUtils.ts)からのものです

interface Func<T> {
    ([...args]: any, args2?: any): T;
}
export function returnType<T>(func: Func<T>) {
    return {} as T;
}


function mapDispatchToProps(dispatch: RootDispatch, props:OwnProps) {
  return {
    onFinished() {
      dispatch(action(props.id));
    }
  }
}

const dispatchGeneric = returnType(mapDispatchToProps);
type DispatchProps = typeof dispatchGeneric;

4

これを行う方法はありません(これを追加する作業項目の追跡については、https://github.com/Microsoft/TypeScript/issues/6606を参照してください)。

一般的な回避策は、次のようなものを書くことです。

var dummy = false && test();
type t2 = typeof dummy;

3
この回避策はもう機能しないと思います。おそらく、制御フローベースの型分析が原因です。
JKillian 2017年

3
あなたが正しいです@JKillianあなたは簡単に活字体をだますことができますが、一例では、もはや作品を提案していない!1代わりにfalse- typescriptlang.org/play/...
DanielM

3

編集:これはTS 2.8ではもう必要ありません!ReturnType<F>戻り値の型を示します。受け入れられた答えを参照してください。


私が使用している以前の回答のいくつかの変形でstrictNullChecksあり、推論体操で機能し、少し隠します。

function getReturnType<R>(fn: (...args: any[]) => R): R {
  return {} as R;
}

使用法:

function foo() {
  return {
    name: "",
    bar(s: string) { // doesn't have to be shorthand, could be `bar: barFn` 
      return 123;
    }
  }
}

const _fooReturnType = getReturnType(foo);
export type Foo = typeof _fooReturnType; // type Foo = { name: string; bar(s: string): number; }

それはない呼び出すgetReturnType関数が、それはしない元の関数を呼び出します。あなたgetReturnTypeを使用して通話を防ぐことができます(false as true) && getReturnType(foo)、IMOすると、混乱が増します。

このメソッドを正規表現の検索/置換とともに使用して、元々JSでこのように記述された約1500のファクトリ関数を持つ古いAngular 1.xプロジェクトを移行し、Fooすべての用途にetcタイプを追加しました...壊れたコード1つ見つけます。:)


ありがとう!悲しいことに、この量の言い回しが必要ですが、少なくとも機能します。
ecmanaut 2018年

@ecmanautもうこれは必要ありません!TS 2.8ではReturnType<F>、関数の戻り値の型を取得するために使用できます。:)
アーロンビール2018年

ここでの答えは、質問の例の基質とは大きく異なり、関数の返される型である型を生成する方法を理解するのが困難になっていますtest。この答えは、名前testを変更するだけの場合foo(そして、キックの場合は、より複雑な戻り値の型を生成するため)に役立つものの1つでした。元の例にそれらを適用する方法をすでに知っている場合は、受け入れられた回答とコメントの両方がおそらく素晴らしいでしょう。(ここでは深夜であり、完全なReturnTypeの例の構文がどのようになるかはすぐにはわかりません。)
ecmanaut 2018年

1

問題の関数がユーザー定義クラスのメソッドである場合、Reflectメタデータと組み合わせてメソッドデコレータを使用して、実行時に戻り値の型(コンストラクタ関数)を決定できます(そして、それを使用して、必要に応じて実行ます)。

たとえば、コンソールにログを記録できます。

function logReturnType(
    target: Object | Function,
    key: string,
    descriptor: PropertyDescriptor
): PropertyDescriptor | void {
    var returnType = Reflect.getMetadata("design:returntype", target, key);

    console.log(returnType);
}

このメソッドデコレータを選択したメソッドにスナップするだけで、メソッド呼び出しから返されると思われるオブジェクトのコンストラクタ関数への正確な参照が得られます。

class TestClass {
    @logReturnType // logs Number (a string representation)
    public test(): number {
        return 42;
    }
}

ただし、このアプローチにはいくつかの注目すべき制限があります。

  • そのように装飾されたメソッドで戻り値の型を明示的に定義する必要があります。そうしないとReflect.getMetadata、から未定義になります。
  • コンパイル後にも存在する実際の型のみを参照できます。つまり、インターフェースやジェネリックはありません

また、デコレータとリフレクトメタデータはどちらもこの投稿の執筆時点では実験的な機能であるため、typescriptコンパイラには次のコマンドライン引数を指定する必要があります。

--emitDecoratorMetadata --experimentalDecorators

0

悲しいことに実行せずに関数の戻り値の型を取得する方法はありません。これは、TypeScriptがJSにコンパイルされると、タイプに関するすべての情報が失われるためです。


3
TSが実行時に型情報を失うことは技術的には真実ですが、OPはコンパイル時に関数の型を判別するため、この事実は関係ありません。これはReturnType<T>、TypeScriptに実装されたことでさらに明白になりました。
thedayturns 2018

0

私は次のことを思いつきました。これはうまく機能しているようです。

function returnType<A, B, Z>(fn: (a: A, b: B) => Z): Z
function returnType<A, Z>(fn: (a: A) => Z): Z
function returnType<Z>(fn: () => Z): Z
function returnType(): any {
    throw "Nooooo"
}

function complicated(value: number): { kind: 'complicated', value: number } {
    return { kind: 'complicated', value: value }
}

const dummy = (false as true) && returnType(complicated)
type Z = typeof dummy

1
とにかくそれらを捨てているので、関数argsは一般的である必要はないと思います。fn: (...args: any[]) => Zは、あなたが必要とすることすべてです。
アーロンビール2017
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.