Abrufen des Rückgabetyps einer Funktion


92

Ich habe folgende Funktion:

function test(): number {
    return 42;
}

Ich kann den Typ der Funktion erhalten, indem ich typeof:

type t = typeof test;

Hier twird sein () => number.

Gibt es eine Möglichkeit, den Rückgabetyp der Funktion zu erhalten? Ich möchte tsein numberstatt () => number.


2
Nein, sowieso nicht mit Art. typeof gibt nur "function" zurück ( mein Gehäuse ist möglicherweise falsch ).
Igor

Es wäre großartig, wenn dies möglich würde. Manchmal definieren Sie Typen, wenn Sie sie aus einer Funktion herausschieben, und es gibt keine (nette) Möglichkeit, diese Struktur zu erfassen.
Jørgen Tvedt

Antworten:


146

BEARBEITEN

Ab TypeScript 2.8 ist dies offiziell mit möglich 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[]

Siehe hier für Details.

TypeScript ist großartig!


Old-School-Hack

Ryans Antwort funktioniert leider nicht mehr. Aber ich habe es mit einem Hack modifiziert, über den ich mich unangemessen freue. Erblicken:

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

Es funktioniert, indem false in den Literalwert true umgewandelt wird, sodass das Typsystem den Rückgabewert für den Typ der Funktion hält. Wenn Sie den Code jedoch tatsächlich ausführen, wird false kurzgeschlossen.

TypeScript ist das Beste. : D.


46
Jesus, das ist schrecklich.
John Weisz

für Flow: const DUMMY = ((null: any): Object) && fn () Exporttyp Type = typeof DUMMY
David Xu

Irgendeine Idee warum, wenn ich 2.8 installiere und versuche, erhalte ich den folgenden Fehler : Cannot find name 'ReturnType'? mit vscode
NSjonas

1
@NSjonas Sie müssen vscode anweisen, die andere Version zu verwenden. Ich denke an die Statusleiste unten rechts.
Meirion Hughes

10
Ich musste tun ReturnType<typeof fn>(die Antwort impliziert, dass ReturnType<fn>es genug ist).
spacek33z

44

Der einfachste Weg in TypeScript 2.8:

const foo = (): FooReturnType => {
}

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

5

Der folgende Code funktioniert ohne Ausführung der Funktion. Es stammt aus der React-Redux-Typoskript-Bibliothek ( 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

Es gibt keine Möglichkeit, dies zu tun (siehe https://github.com/Microsoft/TypeScript/issues/6606 für die Workitem-Verfolgung, die dies hinzufügt).

Eine übliche Problemumgehung besteht darin, Folgendes zu schreiben:

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

2
Ich denke nicht, dass diese Problemumgehung mehr funktioniert - wahrscheinlich aufgrund einer auf Kontrollfluss basierenden Typanalyse.
JKillian

2
@JKillian Sie richtig sind, schlug das Beispiel funktioniert nicht mehr, obwohl man leicht Typoskript mit Trick kann !1statt false- typescriptlang.org/play/...
DanielM

3

Edit: Dies wird mit TS 2.8 nicht mehr benötigt! ReturnType<F>gibt den Rückgabetyp an. Siehe akzeptierte Antwort.


Eine Variante einiger der vorherigen Antworten, die ich verwende, die in strictNullChecksder Inferenzgymnastik funktioniert und diese ein wenig verbirgt:

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

Verwendung:

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; }

Es ist rufen Sie die getReturnTypeFunktion, aber es ist nicht die ursprüngliche Funktion aufrufen. Sie könnten die getReturnTypeVerwendung des Anrufs verhindern , (false as true) && getReturnType(foo)aber IMO macht dies nur verwirrender.

Ich habe diese Methode nur mit einigen regulären Ausdrücken / Ersetzen verwendet, um ein altes Angular 1.x-Projekt zu migrieren, bei dem ~ 1500 Factory-Funktionen wie diese ursprünglich in JS geschrieben wurden, und die Fooetc-Typen zu allen Verwendungszwecken hinzugefügt haben ... erstaunlich, dass der Code kaputt ist wird finden. :) :)


Vielen Dank! Obwohl es traurig ist, dass es so viel Wortschatz braucht, funktioniert es zumindest!
Ecmanaut

@ecmanaut Das brauchen wir nicht mehr! In TS 2.8 können Sie ReturnType<F>einen Funktionsrückgabetyp abrufen. :)
Aaron Beall

Die Antworten hier weichen stark vom Beispielsubstrat der Frage ab, was es schwierig macht, herauszufinden, wie ein Typ erzeugt werden kann, der der zurückgegebene Typ der Funktion ist test. Diese Antwort war eine der nützlichsten für nur Umbenennung testzu foo(und zwar eine komplexere Rückgabetyp, für Kicks produzierend). Sowohl die akzeptierte Antwort als auch Ihr Kommentar sind wahrscheinlich großartig, wenn Sie bereits wissen, wie Sie sie auf das ursprüngliche Beispiel anwenden können. (Es ist spät in der Nacht hier und mir nicht sofort klar, wie die Syntax eines vollständigen ReturnType-Beispiels aussehen würde.)
Ecmanaut

1

Wenn es sich bei der fraglichen Funktion um eine Methode einer benutzerdefinierten Klasse handelt, können Sie Methodendekoratoren in Verbindung mit Reflect Metadata verwenden , um den Rückgabetyp (Konstruktorfunktion) zur Laufzeit zu bestimmen (und damit zu tun, was Sie für richtig halten).

Sie können es beispielsweise in der Konsole protokollieren:

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

    console.log(returnType);
}

Rasten Sie diesen Methodendekorator einfach auf eine Methode Ihrer Wahl ein, und Sie haben den genauen Verweis auf die Konstruktorfunktion des Objekts, das angeblich vom Methodenaufruf zurückgegeben wird.

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

Dieser Ansatz weist jedoch einige bemerkenswerte Einschränkungen auf:

  • Sie müssen den Rückgabetyp für eine als solche dekorierte Methode explizit definieren, sonst werden Sie undefiniert von Reflect.getMetadata,
  • Sie können nur auf tatsächliche Typen verweisen, die auch nach der Kompilierung vorhanden sind. Das heißt, keine Schnittstellen oder Generika

Außerdem müssen Sie die folgenden Befehlszeilenargumente für den Typoskript-Compiler angeben, da sowohl Dekoratoren als auch Reflect-Metadaten zum Zeitpunkt des Schreibens dieses Beitrags experimentelle Funktionen sind:

--emitDecoratorMetadata --experimentalDecorators

0

Es gibt keine Möglichkeit, den Rückgabetyp der Funktion abzurufen, ohne sie traurig auszuführen. Dies liegt daran, dass Sie beim Kompilieren von TypeScript in JS alle Informationen zum Typ verlieren.


3
Obwohl es technisch wahr ist, dass TS zur Laufzeit Typinformationen verliert, ist diese Tatsache nicht relevant, da OP den Typ der Funktion zur Kompilierungszeit bestimmen möchte. Dies ist jetzt noch offensichtlicher, da ReturnType<T>es in TypeScript implementiert wurde.
Tag dreht sich

0

Ich habe mir Folgendes ausgedacht, was anscheinend gut funktioniert:

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
Ich denke nicht, dass die Funktionsargumente generisch sein müssen, da Sie sie sowieso wegwerfen. fn: (...args: any[]) => Zist alles, was du brauchst.
Aaron Beall
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.