Communardo Software GmbH, Kleiststraße 10 a, D-01129 Dresden
+49 (0) 351/8 33 82-0

TypeScript-Tipps: Überladene Funktionen durch Interfaces beschreiben

Wie lassen sich Schnittstellen beschreiben, die überladene Funktionen als Parameter entgegennehmen oder als Wert zurückgeben? Der Beitrag zeigt anhand eines Beispiels, wie dies in TypeScript ohne Probleme gelingt.

TypeScript bietet enormes Potential, den Entwickler während der Entwicklung durch Typisierung zu unterstützen. Es ist damit klar das bessere JavaScript. Die Reihe TypeScript-Tipps setzt einzelne Features der Sprache in den Fokus. Heute sind dies Interfaces.

Wir bauen auf einem vorherigen Beitrag der Reihe TypeScript-Tipps auf und schauen erneut auf überladene Funktionen. Wie lassen sich Schnittstellen beschreiben, die überladene Funktionen als Parameter entgegennehmen oder als Wert zurückgeben? Der Beitrag zeigt anhand eines Beispiels, wie dies in TypeScript ohne Probleme gelingt.

Wir bauen eine Factory für Funktionen

Schauen wir kurz auf den Code, der im Blogbeitrag „TypeScript-Tipps: Funktionen überladen“ entstanden ist:

Zur Erinnerung: der obige Code deklariert die zweifach überladene Funktion AddOne unter Verwendung von Function Types. Wen die Herleitung und die Hintergründe interessieren, der schaut am besten kurz in den Blogbeitrag rein.

Nehmen wir an, die Funktion AddOne kann unterschiedliche Implementierungen besitzen und soll daher durch eine Art Fabrikfunktion bereitgestellt werden:

Die erste Zeile deklariert AddOneFactory als Funktion, die wiederum eine Funktion mit Signatur (value: any) => any zurückgibt. Die zweite Zeile implementiert diese Fabrikfunktion so, dass als Ergebnis unsere AddOne-Funktion zurückgegeben wird. Diese könnte nun jederzeit durch eine andere Implementierung ersetzt werden.

So sieht die Verwendung der Fabrikfunktion aus:

Der Aufruf von AddOneFactory() gibt eine AddOne-Funktion zurück, die anschließend aufgerufen werden kann.

Wo ist der Haken?

Wir haben durch die Deklaration der Fabrikfunktion jegliche Typinformationen verloren, die wir der konkreten Implementierung von AddOne mitgegeben hatten. Sowohl Parameter als auch Rückgabewert sind vom Typ any.

Aber das geht besser.

Funktionen durch Interfaces beschreiben

Interfaces dienen in TypeScript dazu, Datentypen zu beschreiben, ohne (im Unterschied zu Klassen) eine Implementierung vorzugeben.

Ein interessantes Feature von Interfaces ist die Beschreibung von Function Types, also Signaturen von Funktionen. Fangen wir mit einem einfachen Beispiel an:

Die Syntax ist etwas gewöhnungsbedürftig. Das Interface IFunctionTypeDescriptor beschreibt in Zeile 2 die Signatur einer Funktion ohne Namen: (): string. Anschließend wird in Zeile 5 eine Variable func deklariert, die vom Typ IFunctionTypeDescriptor ist. Der Wert dieser Variable muss nun genau der im Interface deklarierten Signatur entsprechen, also eine Funktion ohne Parameter, die Text zurückgibt.

In Zeile 6 wird eine solche Funktion zugewiesen. An die Lambda-Schreibweise sollten sich alle SharePoint Framework-Erfahrenen inzwischen gewöhnt haben.

In Zeile 7 nun wird die Funktion aufgerufen und die Ausgabe erfolgt.

Und warum betreiben wir diesen Aufwand eigentlich? Weil wir damit wieder einmal Fehler frühzeitig erkennen können. Gibt beispielsweise die Funktion func keinen Text zurück, sondern eine Zahl, dann weist Visual Studio Code während der Entwicklung darauf hin:

Der geforderte Rückgabewert string und der in der Implementierung von func verwendete Typ number sind nicht kompatibel.

Was aber, wenn sowohl string, als auch number erlaubt sein sollen?

Überladene Funktionen durch Interfaces beschreiben

Jetzt schließt sich der Kreis zurück zu unserer AddOne-Fabrikfunktion. Wir können ein Interface verwenden, um diese Funktion korrekt zu typisieren.

Vorab das komplette Code-Beispiel, welches wir der Reihe nach betrachten:

Die Zeilen 1-9 sind unverändert.

In den Zeilen 11-14 wird jetzt mit Hilfe eines Interfaces und Function Types beschrieben, welche überladene Funktion unsere Fabrikfunktion erzeugt. Hier sind alle überladenen Signaturen der erzeugten Funktion aufgeführt.

In Zeile 16 wird die Fabrikfunktion BetterAddOneFactory deklariert. Diese gibt selbst eine Funktion zurück, die wie durch IAddOneFactoryResult beschrieben überladen ist.

In Zeile 17 wird wie gehabt die Fabrikfunktion implementiert.

Und warum machen wir das Ganze? Diese Frage wird in Zeile 20 beantwortet, wenn wir uns die Autovervollständigung in Visual Studio Code anschauen:

Die von der Fabrikfunktion erzeugte Funktion ist korrekt typisiert. Außerdem wird sichergestellt, dass die von der Fabrikfunktion erzeugten Implementierungen immer wie erwartet überladen sind.

Zusammenfassung

Wir haben gelernt, wie sich überladene Funktionen durch Interfaces beschreiben lassen.

Dabei haben wir eine Fabrikfunktion als Anwendungsbeispiel betrachtet. Diese Fabrikfunktion erzeugt überladene Funktionen mit klar definierten Signaturen, die zur Entwicklungszeit auf Richtigkeit prüfbar sind. Dafür machen wird uns zunutze, dass TypeScript-Interfaces überladene Funktionen beschreiben können.

Happy Coding!

Kommentar hinterlassen