TypeScript Interfaces: von der Syntax bis interface vs type
TypeScript Interfaces
TypeScript Interface: Syntax, extends, Merging, vs type | We.Inc
Baue ein Interface Feld für Feld, sieh den passenden Type Alias und lerne optionale und readonly Member, extends, Declaration Merging und wann ein Type Alias besser passt.
Ein Interface benennt die Form eines Objekts: welche Eigenschaften es hat und welchen Typ jede davon trägt. Markiere eine Eigenschaft mit ? als optional und mit readonly als unveränderlich, baue mit extends auf einer anderen Form auf und öffne denselben Namen erneut, um weitere Member hinzuzufügen. Interfaces hinterlassen im kompilierten JavaScript keine Spur; sie leiten nur den Type Checker.
Interface-Syntax auf einen Blick
Du willst
Schreib
Eine Pflichteigenschaft
name: string;
Eine optionale Eigenschaft
nickname?: string;
Eine Eigenschaft, die nicht neu zugewiesen werden kann
readonly id: number;
Eine Methode
greet(other: User): string;
Beliebig viele String-Keys
[key: string]: number;
Eine andere Form wiederverwenden
interface Admin extends User { ... }
Mehrere Formen kombinieren
interface A extends B, C { ... }
Füge im Generator unten Felder hinzu, setze Häkchen bei optional oder readonly und kopiere das Interface oder den gleichwertigen Type Alias. Die Abschnitte darunter behandeln Erweitern, Merging und die wenigen Fälle, in denen die Wahl zwischen interface und type wirklich zählt.
Benenne das Interface und trag, falls es auf einem anderen aufbaut, dessen Namen in das Feld extends ein.
Füge pro Eigenschaft eine Zeile hinzu und wähle ihren Typ und ob sie optional oder readonly ist.
Lies das Interface und den Type Alias, die der Generator aus denselben Zeilen schreibt.
Kopiere eine der beiden Versionen in deinen Code.
Beginne bei Objektformen mit interface und greif zu type, wenn du eine Union, ein Tuple oder einen Mapped Type brauchst. Genau diese Faustregel schlägt das TypeScript-Handbuch selbst vor.
readonly verhindert nur die Neuzuweisung genau dieser einen Eigenschaft. Darin verschachtelte Objekte können sich trotzdem ändern, und zur Laufzeit wird nichts erzwungen.
Eine optionale Eigenschaft liest sich als ihr Typ oder undefined, prüfe sie also vor der Verwendung oder gib ihr beim Destrukturieren einen Standardwert.
Halte Interface-Namen im Singular und aussagekräftig, etwa Invoice oder CartItem. Ein Präfix wie IInvoice fügt nichts hinzu, was dir der Editor nicht ohnehin sagt.
Häufige Fragen
Sollte ich interface oder type verwenden?
Für Objektformen funktioniert beides, und das Handbuch nennt es größtenteils persönliche Vorliebe und empfiehlt interface, bis du ein Feature brauchst, das nur type hat. Nutze type für Unions, Tuples, primitive Typen sowie Mapped und Conditional Types; nutze interface, wenn du Declaration Merging willst.
Was ist Declaration Merging?
Wenn du ein Interface mit einem bereits existierenden Namen deklarierst, werden ihm Member hinzugefügt, statt dass ein Fehler entsteht. Libraries nutzen das, damit du ihre Typen erweitern kannst, zum Beispiel um Window eine Eigenschaft hinzuzufügen. Ein Type Alias mit wiederholtem Namen ist ein Fehler.
Was passiert, wenn zwei gemergte Interfaces einer Eigenschaft unterschiedliche Typen geben?
Der Compiler meldet einen Fehler. Bei einer Intersection zweier Typen wird der Konflikt nicht an der Deklaration gemeldet; die Eigenschaft wird zu never, was erst später auffällt und schwerer nachzuverfolgen ist.
Kann ein Interface eine Funktion oder ein Array beschreiben?
Ja. Eine Call Signature wie (value: string): boolean beschreibt eine Funktion, und eine Index Signature wie [index: number]: string beschreibt ein Array-ähnliches Objekt. In einfachen Fällen liest sich ein Type Alias wie type Check = (v: string) => boolean klarer.
Existieren Interfaces zur Laufzeit?
Nein. Sie werden zu nichts kompiliert, du kannst also weder instanceof mit einem Interface verwenden noch seine Keys auflisten. Prüfe Werte mit einem Guard zur Laufzeit und annotiere dessen Rückgabetyp mit dem Interface.
extends im Vergleich zu Intersections
interface Bear extends Animal übernimmt jeden Member von Animal und lässt dich weitere hinzufügen. Ein Type Alias erledigt dasselbe mit einer Intersection, type Bear = Animal & { honey: boolean }.
Das Handbuch merkt an, dass extends für den Compiler schneller sein kann und widersprüchliche Member genau dort meldet, wo sie deklariert werden. Intersections sind praktisch, um Formen zusammenzufügen, die dir nicht gehören.
Optional, readonly und Index Signatures
Ein Fragezeichen nach dem Namen macht eine Eigenschaft optional, und ihr Lesetyp schließt undefined ein. readonly verhindert Zuweisungen an diese Eigenschaft nach der Erstellung, ist aber nur eine Prüfung zur Compile-Zeit.
Eine Index Signature, [key: string]: number, beschreibt Wörterbücher, deren Keys du nicht im Voraus auflisten kannst. Benannte Eigenschaften im selben Interface müssen zum Werttyp der Index Signature passen.
Merging, um globale Typen und Library-Typen zu erweitern
Weil Interfaces gemergt werden, kann eine Deklarationsdatei eingebauten Formen eigene Felder hinzufügen. Ein häufiger Fall ist, Window eine Eigenschaft zu geben, die ein Skript setzt, damit das Lesen ohne Cast die Typprüfung besteht.
interface vs Type Alias
Was jede Deklaration kann
Funktion
Type Alias
Eine Objektform beschreiben
Ja
Ja
Member durch erneutes Deklarieren des Namens hinzufügen
Ja (Merging)
Nein, Fehler wegen doppeltem Bezeichner
Auf einer anderen Form aufbauen
Intersection mit &
Union aus Formen oder Literalen
Nein
Ja
Einen primitiven Typ oder ein Tuple benennen
Nein
Ja
Mapped und Conditional Types
Nein
Ja
Muster, die du oft schreiben wirst
Ein und mehrere Interfaces erweitern
Admin bekommt jeden Member von User plus eigene; Card kombiniert zwei unabhängige Formen.
Declaration Merging bei Window
In einer .d.ts-Datei abgelegt, besteht window.appVersion überall die Typprüfung.
Index Signature und Call Signature
Scores akzeptiert jeden String-Key; Validator beschreibt einen aufrufbaren Wert.
Interface-Generator
Name des Interfaces
Extends (optional)
Name von Eigenschaft {n}
Typ von Eigenschaft {n}
Optional
Readonly
Eigenschaft {n} entfernen
Eigenschaft hinzufügen
Als Interface
Als Type Alias
Interface kopieren
Type kopieren
Namen müssen mit einem Buchstaben, $ oder _ beginnen und dürfen nur Buchstaben, Ziffern, $ oder _ enthalten.