ProgrammierungFrontend/Fullstack Entwickler

Wie funktioniert der Typ ReturnType<T> in TypeScript, wie unterscheidet er sich von der manuellen Ableitung des Rückgabetyps einer Funktion und welche Risiken/Vorteile ergeben sich bei seiner Verwendung?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort.

Geschichte der Frage

Mit der Entwicklung von TypeScript entstand die Notwendigkeit, den Rückgabewert einer Funktion automatisch zu extrahieren, insbesondere für große Projekte mit vielen miteinander verbundenen Funktionen. Zu diesem Zweck wurde der Utility-Typ ReturnType<T> eingeführt, der seit der Version TypeScript 2.8 in der Standardbibliothek verfügbar ist.

Problem

In großen und komplexen Projekten kann es schwierig sein, die Typen aktuell zu halten — wenn man den Rückgabewert jeder Funktion manuell angibt und zur gleichen Zeit die Signaturen ändert, kann leicht Inkonsistenz entstehen, wenn die Signaturen und Implementierungen nicht übereinstimmen. Darüber hinaus, wenn eine Funktion eine komplexe Struktur zurückgibt, ist es nicht immer einfach, die Beschreibung des Rückgabetyps an allen Verwendungsstellen manuell zu synchronisieren.

Lösung

Der Utility-Typ ReturnType<T> extrahiert automatisch den Rückgabewert von Funktionen und kann zur Typisierung des Ergebnisses eines Funktionsaufrufs an beliebiger Stelle im Code verwendet werden. Dies reduziert die Menge an manueller Pflege der Typinfrastruktur und minimiert Fehler im Zusammenhang mit der Diskrepanz zwischen dem beschriebenen und dem tatsächlichen Rückgabewerttyp.

Beispielcode:

function createUser(name: string, age: number) { return { name, age, created: new Date() }; } type User = ReturnType<typeof createUser>; // User: { name: string; age: number; created: Date; }

Wichtige Eigenschaften:

  • Extrahiert automatisch den Rückgabewert von Funktionen (einschließlich Generics) und beseitigt Code-Duplikationen.
  • Verringert die Wahrscheinlichkeit von Fehlern bei Änderungen der Funktionssignatur oder der Struktur des Rückgabewerts.
  • Funktioniert nicht mit Funktionsüberladungen — extrahiert nur den allgemeinen (breiten) Rückgabewerttyp.

Fangfragen.

Kann man ReturnType mit Methoden von Klassen verwenden?

Ja, aber es ist wichtig, den Kontext zu beachten: Wenn die Methode eine Eigenschaft des Objekts mit einer Funktion ist, verwenden Sie ReturnType<obj['methode']>.

Beispielcode:

class MyClass { foo(x: number) { return x * 2; } } type FooReturn = ReturnType<MyClass['foo']>; // Typfehler! // Sollte so sein: type FooReturn = ReturnType<(x: number) => number>; // number // Oder die Methode als Funktion herausziehen: const obj = new MyClass(); type FooReturn2 = ReturnType<typeof obj.foo>;

Was gibt ReturnType für Funktionen mit void/never zurück?

Für eine Funktion mit dem deklarierten Typ void gibt ReturnType void zurück. Für never — never.

Beispielcode:

function doNothing(): void {} type Result = ReturnType<typeof doNothing>; // void

Funktioniert ReturnType mit Funktionsüberladungen?

Nein, ReturnType extrahiert den Rückgabewerttyp der "Implementierung" selbst und nicht von allen Überladungen. Wenn es mehrere Überladungen gibt, wird der beschriebene Implementierungstyp verwendet.

Beispielcode:

function func(x: number): number; function func(x: string): string; function func(x: any): any { return x } type RT = ReturnType<typeof func>; // any

Typfehler und Anti-Patterns

  • Verwendung von ReturnType mit überladenen Funktionen führt zu unerwarteten Typen.
  • Vergessen, dass ReturnType den Rückgabewerttyp einer Promise nicht berechnet — benötigt Awaited oder manuelle Arbeit.
  • Vollkommene Abhängigkeit von ReturnType bei der Änderung der Logik einer Funktion ohne Aktualisierung anderer abhängiger Code-Teile.

Beispiel aus der Praxis

Negativer Fall

In einem Projekt wird ein manueller Typ für das zurückgegebene Objekt einer Funktion deklariert. Die Funktion ändert sich — der Typ wird nicht aktualisiert, Aufrufe schlagen zur Laufzeit fehl.

Vorteile:

  • Typen sind einfach zu lesen und können manuell geregelt werden.

Nachteile:

  • Werden schnell veraltet, es entsteht eine "Drift" zwischen Typen und tatsächlichem API.

Positiver Fall

Wechseln zu ReturnType überall dort, wo der von einer Funktion zurückgegebene Wert verwendet wird. Im Falle von Änderungen ist der Typ immer aktuell.

Vorteile:

  • Minimales Duplikat, typisierte Übereinstimmung mit der tatsächlichen Implementierung.

Nachteile:

  • Es kann Schwierigkeiten bei der Verständnis der Typ Magie bei Neulingen geben.