Pick und Omit
Eine Gilde führt ein Register ihrer Mitglieder. Jedes Mitglied hat eine id, eine Zahl, die die Gilde beim Eintritt vergibt:
type HeldenKlasse = "Krieger" | "Magier" | "Waldläufer";
interface Held {
id: number;
name: string;
heldenKlasse: HeldenKlasse;
stufe: number;
}Eine Funktion, die ein neues Mitglied aufnimmt, bekommt einen Helden ohne id, weil die Gilde die id selbst vergibt. Du könntest ein zweites Interface mit den anderen drei Eigenschaften schreiben, musst dann aber beide von Hand synchron halten. Schreibe stattdessen Omit<Held, "id">: den Typ Held ohne die Eigenschaft id. Das zweite Typargument ist der Name der Eigenschaft, als Literaltyp in Anführungszeichen. Um mehrere Eigenschaften wegzulassen, schreibst du eine Union: Omit<Held, "id" | "stufe">.
type HeldenKlasse = "Krieger" | "Magier" | "Waldläufer";
interface Held {
id: number;
name: string;
heldenKlasse: HeldenKlasse;
stufe: number;
}
let naechsteId = 1;
function fuegeMitgliedHinzu(neuerHeld: Omit<Held, "id">): Held {
const held: Held = { id: naechsteId, ...neuerHeld };
naechsteId++;
return held;
}
console.log(fuegeMitgliedHinzu({ name: "Kira", heldenKlasse: "Waldläufer", stufe: 3 }));
console.log(fuegeMitgliedHinzu({ name: "Bruno", heldenKlasse: "Krieger", stufe: 5 }));Das gibt aus:
{ id: 1, name: "Kira", heldenKlasse: "Waldläufer", stufe: 3 }
{ id: 2, name: "Bruno", heldenKlasse: "Krieger", stufe: 5 }Der neue Held wird mit dem Spread aus der vorigen Übung zusammengesetzt: zuerst die id, dann alle Eigenschaften von neuerHeld. Das Ergebnis hat alle vier Eigenschaften, ist also ein Held. Und wenn du die Funktion aufrufst, prüft TypeScript das Argument gegen Omit<Held, "id">: Eine id ist jetzt ein Fehler, und eine fehlende Eigenschaft auch:
fuegeMitgliedHinzu({ id: 7, name: "Kira", heldenKlasse: "Waldläufer", stufe: 3 });
fuegeMitgliedHinzu({ name: "Lena", heldenKlasse: "Magier" });main.ts(18,22): error TS2353: Object literal may only specify known properties, and 'id' does not exist in type 'Omit<Held, "id">'.
main.ts(19,20): error TS2741: Property 'stufe' is missing in type '{ name: string; heldenKlasse: "Magier"; }' but required in type 'Omit<Held, "id">'.Pick funktioniert andersherum: Pick<Held, "name" | "stufe"> behält nur die aufgelisteten Eigenschaften, ist also { name: string; stufe: number }. Das passt zu einer Funktion, die nur einen Teil eines Objekts braucht:
function abzeichen(held: Pick<Held, "name" | "stufe">): string {
return `${held.name}, Stufe ${held.stufe}`;
}Du kannst ihr einen ganzen Held übergeben: Er hat einen Namen und eine Stufe, und den Rest beachtet die Funktion nicht. Oder ein kleineres Objekt wie { name: "Lena", stufe: 1 }. Innerhalb der Funktion gibt es aber nur name und stufe. held.heldenKlasse wäre ein Fehler: Property 'heldenKlasse' does not exist on type 'Pick<Held, "name" | "stufe">'. (TypeScript sortiert die Texte in so einer Union alphabetisch, deshalb können sie in einer anderen Reihenfolge stehen, als du sie geschrieben hast, wie das Beispiel bei den typischen Fehlern zeigt.)
Typische Fehler:
- Ein Tippfehler bei
Pickwird entdeckt, denn die Namen müssen Schlüssel vonHeldsein. Das ist eine Einschränkung mitkeyof, wie in der Lektion „Generics“:main.ts(10,37): error TS2344: Type '"name" | "stufee"' does not satisfy the constraint 'keyof Held'. Type '"stufee"' is not assignable to type 'keyof Held'. Did you mean '"stufe"'? - Ein Tippfehler bei
Omitwird nicht entdeckt!Omit<Held, "idd">lässt nichts weg, dieidbleibt, und es gibt keinen Fehler. Prüfe die Namen beiOmitgenau. - Mehrere Namen sind ein Typargument, eine Union:
Pick<Held, "name" | "stufe">. Mit einem Komma,Pick<Held, "name", "stufe">, meldet TypeScriptGeneric type 'Pick' requires 2 type argument(s).
So schreibst du es:
Anweisungen
- 1
Schreibe unter den Kommentar
// Fügt ein neues Mitglied hinzueine FunktionfuegeMitgliedHinzu(neuerHeld: Omit<Held, "id">): Held. Sie erstellt den Helden mit deridausnaechsteId, erhöhtnaechsteIdum eins, fügt den Helden mitpushzugildehinzu und gibt ihn zurück. - 2
Füge unter der Funktion zwei Mitglieder hinzu und gib aus, was
fuegeMitgliedHinzuzurückgibt: Kira, eine Waldläuferin der Stufe 3, und Bruno, ein Krieger der Stufe 5:{ id: 1, name: "Kira", heldenKlasse: "Waldläufer", stufe: 3 } { id: 2, name: "Bruno", heldenKlasse: "Krieger", stufe: 5 } - 3
Schreibe darunter eine Funktion
beschreibeMitglied(held: Pick<Held, "name" | "stufe">): string, die den Namen und die Stufe so zurückgibt:Kira (Stufe 3). - 4
Gib darunter in einer
for...of-Schleife aus, wasbeschreibeMitglied()für jedes Mitglied vongildezurückgibt. Gib danach auchbeschreibeMitglied({ name: "Lena", stufe: 1 })aus: Lena ist noch nicht in der Gilde, aber die Funktion braucht nur einen Namen und eine Stufe. Das Programm endet so:Kira (Stufe 3) Bruno (Stufe 5) Lena (Stufe 1)