Intersections
Ein Boss ist ein Monster mit etwas Extra: Er kämpft in Phasen und gibt eine große Belohnung. In der Lektion „Objekttypen“ würdest du interface Boss extends Monster { … } schreiben. Mit type schreibt man dasselbe mit &:
type Monster = {
name: string;
leben: number;
angriff: number;
};
type Boss = Monster & { phase: number; belohnung: number };
const drache: Boss = { name: "Drache", leben: 300, angriff: 25, phase: 1, belohnung: 500 };
console.log(`${drache.name}: Leben ${drache.leben}, Phase ${drache.phase}`);Das gibt aus:
Drache: Leben 300, Phase 1A & B ist ein Intersection-Typ (Schnitttyp): Ein Wert dieses Typs muss gleichzeitig ein A und ein B sein, hat also alle Eigenschaften von A und alle Eigenschaften von B. Der Name klingt vielleicht seltsam: Der Schnitt zweier Dinge ist normalerweise weniger als jedes von ihnen, nicht mehr. Aber weniger sind die Werte: nur die Objekte, die zu beiden Typen passen. Und diese Objekte haben mehr Eigenschaften.
Vergleiche das mit einer Union, die du aus der Lektion „Unions und Eingrenzen“ kennst. A | B ist das eine oder das andere, also kannst du, bevor du es eingrenzt, nur verwenden, was beide haben. A & B ist beides gleichzeitig, also kannst du alles verwenden.
Wenn einem Boss etwas fehlt, sagt die Meldung, welcher Teil der Intersection es verlangt:
const troll: Boss = { name: "Troll", leben: 80, angriff: 9 };main.ts(10,7): error TS2322: Type '{ name: string; leben: number; angriff: number; }' is not assignable to type 'Boss'.
Type '{ name: string; leben: number; angriff: number; }' is missing the following properties from type '{ phase: number; belohnung: number; }': phase, belohnungEin Boss ist auch ein Monster, darf also überall stehen, wo ein Monster erwartet wird, zum Beispiel in einem Monster[], genau wie bei extends.
Welches solltest du also verwenden? Bei Objekttypen machen beide dieselbe Arbeit. interface … extends ist oft klarer und meldet einen Konflikt gleich dort, wo du das Interface deklarierst: Hätte Monster id: number und der Boss id: string, bekämst du Interface 'Boss' incorrectly extends interface 'Monster'. Eine Intersection meldet den Konflikt nicht, und der Typ der Eigenschaft wird zu never, einem Typ ohne Werte. Du merkst es erst, wenn du ein Objekt erstellst: Type 'number' is not assignable to type 'never'. Dafür verbindet & beliebige Typen, zum Beispiel zwei Typen, die du schon hast, type Soeldner = Held & Haendler;, oder einen Typ mit einem Utility-Typ, Omit<Held, "id"> & { beigetreten: number }.
Ein typischer Fehler ist | statt &. Mit type Boss = Monster | { phase: number }; ist ein Boss nur eins von beiden, und boss.phase ergibt einen Fehler:
main.ts(10,17): error TS2339: Property 'phase' does not exist on type 'Boss'.
Property 'phase' does not exist on type 'Monster'.So schreibst du es:
Anweisungen
- 1
Erstelle unter
Monsterden TypBoss: einMonstermit zwei weiteren Eigenschaften,phaseundbelohnung, beide Zahlen. Verwende&. - 2
Erstelle unter den Monstern eine Konstante
drachevom TypBoss: der Drache mit 300 Leben, Angriff 25, Phase 1 und einer Belohnung von 500. Schreibe dann eine FunktionbeschreibeBoss(boss: Boss): stringund gib aus, was sie für den Drachen zurückgibt:Drache (Leben 300, Angriff 25), Phase 1, Belohnung 500 Gold - 3
Erstelle darunter eine Konstante
monstervom TypMonster[]mit dem Goblin, dem Wolf und dem Drachen und gib in einerfor...of-Schleife den Namen und den Angriff jedes Monsters aus:Goblin: Angriff 3 Wolf: Angriff 5 Drache: Angriff 25