Generische Funktionen
Hier ist die Funktion aus der vorigen Übung, einmal für jeden Typ geschrieben:
function erstes<T>(elemente: T[]): T | undefined {
return elemente[0];
}
const namen = ["Kira", "Bruno", "Lena"];
const preise = [30, 12, 55];
console.log(erstes(namen));
console.log(erstes(preise));Das gibt aus:
Kira
30Neu ist <T> hinter dem Namen der Funktion. Es deklariert einen Typparameter namens T. Ein gewöhnlicher Parameter steht für einen Wert, der bei jedem Aufruf ein anderer sein kann; ein Typparameter steht für einen Typ, der bei jedem Aufruf ein anderer sein kann. Der Rest der Funktion verwendet T wie jeden anderen Typ: elemente: T[] ist ein Array von Ts, und die Funktion gibt ein T zurück (oder undefined, dazu unten mehr).
Bei jedem Aufruf setzt TypeScript T aus dem Argument ein. In erstes(namen) ist namen ein string[], also ist T gleich string und das Ergebnis ein string | undefined. In erstes(preise) ist T gleich number. Eine Funktion mit einem Typparameter heißt generische Funktion. Wie die any-Version funktioniert sie mit jedem Array. Anders als sie vergisst TypeScript nicht, was hineingegangen ist:
function erstes<T>(elemente: T[]): T | undefined {
return elemente[0];
}
const namen = ["Kira", "Bruno", "Lena"];
const preise = [30, 12, 55];
const anfuehrer = erstes(namen);
if (anfuehrer !== undefined) {
console.log(anfuehrer.toUpperCase());
}
const ersterPreis = erstes(preise);
if (ersterPreis !== undefined) {
console.log(ersterPreis.toUpperCase());
}TypeScript meldet:
main.ts(15,29): error TS2339: Property 'toUpperCase' does not exist on type 'number'.anfuehrer.toUpperCase() ist in Ordnung, denn anfuehrer ist ein Text. ersterPreis ist eine Zahl, und TypeScript sagt es dir, bevor das Programm läuft.
Warum T | undefined? Ein leeres Array hat kein erstes Element, also ist elemente[0] dann undefined. TypeScript prüft Array-Indizes nicht, deshalb sagen wir es im Rückgabetyp. Wer erstes aufruft, muss dann den fehlenden Wert behandeln, wie bei find in der Lektion über Unions und Narrowing: mit if (anfuehrer !== undefined), mit anfuehrer?.name oder mit ??. Ohne Prüfung meldet TypeScript 'anfuehrer' is possibly 'undefined'. (TS18048). Beim Ausgeben ist keine Prüfung nötig: console.log(erstes(namen)) ist in Ordnung, denn console.log gibt alles aus, auch undefined.
Der Name T (für „Typ“) ist nur eine Gewohnheit. Jeder Name funktioniert, zum Beispiel function erstes<Element>(elemente: Element[]), aber kurze Namen mit Großbuchstaben sind üblich. Und wie alle Typen verschwinden Typparameter, wenn das Programm läuft. Das JavaScript ist einfach function erstes(elemente) { return elemente[0]; }.
Typische Fehler:
- Den Typparameter nicht deklarieren:
function erstes(elemente: T[])ergibtCannot find name 'T'.(TS2304).Tgibt es erst, wenn du<T>hinter den Namen der Funktion schreibst. - Das Ergebnis verwenden, ohne auf
undefinedzu prüfen (TS18048).
So schreibst du es:
Anweisungen
- 1
Ersetze
ersterHeldundersterTrankdurch eine generische Funktion,erstes<T>(elemente: T[]): T | undefined, und verwende sie an beiden Stellen. TypeScript sagt dir dann, dassanfuehrermöglicherweiseundefinedist: Schreibeanfuehrer?.nameim Template-Literal. Das Programm gibt dieselben Zeilen aus wie zuvor. - 2
Schreibe eine zweite generische Funktion,
letztes<T>(elemente: T[]): T | undefined, die das letzte Element des Arrays zurückgibt:elemente[elemente.length - 1]. - 3
Gib am Ende des Programms mit
letztesden letzten Trank und den Namen des letzten Helden aus, sodass das Programm Folgendes ausgibt:Anführer: Kira Erster Trank: Heiltrank Letzter Trank: Elixier Letzter Held: Bruno