Zwei Deklarationsformen

interface oder type: drei Stellen, an denen es zählt

Für die gewöhnliche Objektform kannst du würfeln. Für Modulerweiterungen, Vereinigungen und die Zuweisbarkeit an eine offene Zuordnung gibt es jeweils genau eine richtige Antwort.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Seit 1997 am Markt Kleine Gruppen Präsenz und Live-Online Zertifizierte Trainer
Worum es geht

Die Diskussion ist länger als die Liste der echten Unterschiede

In fast jedem Team gibt es zu dieser Frage eine Meinung, meistens zwei. Die eine Seite sagt, interface sei die vorgesehene Form für Objekte, die andere, type sei die allgemeinere und man brauche dann nur eine. Beide Seiten haben in dem Bereich recht, auf den sie schauen, und das ist der Grund, warum die Diskussion nie endet: Für den mit Abstand häufigsten Fall, nämlich eine Struktur mit ein paar Feldern, gibt es tatsächlich keinen Unterschied, der sich im Alltag bemerkbar macht.

Teuer wird die Sache erst dort, wo jemand eine der beiden Seiten zur allgemeinen Regel erklärt. Wer nur type erlaubt, steht beim ersten Versuch, den Typ einer fremden Bibliothek zu erweitern, ohne Lösung da, denn dafür braucht es das Zusammenführen gleichnamiger Deklarationen, und das kann nur interface. Wer nur interface erlaubt, kann keine Vereinigung deklarieren, keinen Tupeltyp benennen und keinen Typ aus einem Wert ableiten, und weicht dann auf Konstruktionen aus, die niemand mehr liest.

Der praktische Schaden liegt aber woanders. Wenn beide Formen unabgesprochen nebeneinander benutzt werden, sieht dieselbe Struktur an drei Stellen im Projekt unterschiedlich aus, und im Review geht bei jeder neuen Deklaration Zeit für dieselbe Diskussion drauf. Nicht die Wahl ist das Problem, sondern das Fehlen einer Entscheidung.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt
Der direkte Vergleich

Der direkte Vergleich

interface

die Schreibweise für Objektformen, die sich erweitern und mit gleichnamigen Deklarationen zusammenführen lässt

type

ein Name für einen beliebigen Typ, also auch für Vereinigungen, Tupel und alles, was berechnet wird

Wie beschreibst du eine gewöhnliche Struktur mit fünf Feldern?

interface

Genau dafür gedacht, die Schreibweise ist kurz und in Fehlermeldungen erscheint der Name statt der ausgeschriebenen Form.

type

Funktioniert identisch. Für diesen Fall gibt es keinen Unterschied, der sich im Alltag bemerkbar macht.

Wie ergänzt du den Typ einer fremden Bibliothek um ein eigenes Feld?

interface

Zwei gleichnamige Deklarationen im selben Bereich werden zusammengeführt. Genau darauf bauen Modulerweiterungen auf, etwa um an ein Anfrageobjekt ein eigenes Feld zu hängen.

type

Eine zweite Deklaration desselben Namens meldet einen doppelten Bezeichner. Für diesen Fall gibt es keinen Ersatz.

Wie deklarierst du eine Vereinigung mehrerer Varianten oder ein Tupel?

interface

Geht nicht. Ein interface beschreibt immer eine Objektform, alles andere lässt sich damit nicht ausdrücken.

type

Der eigentliche Zweck der Schreibweise: Sie gibt jedem Typ einen Namen, gleich ob Vereinigung, Tupel, Funktionstyp oder etwas Abgeleitetes.

Was passiert, wenn beim Erweitern zwei Felder denselben Namen mit verschiedenen Typen haben?

interface

Der Compiler meldet den Widerspruch dort, wo die Erweiterung steht. Du siehst den Fehler beim Schreiben der Deklaration.

type

Die Deklaration bleibt in jedem Fall still. Widersprechen sich zwei einfache Typen, etwa Zeichenkette gegen Zahl, wird das Feld zu never. Sind beide Felder Objekttypen, entsteht eine Verschneidung, die kein Wert mehr erfüllen kann. Beides fällt erst dort auf, wo jemand einen Wert zuweist.

Passt die Form zu einem Parameter vom Typ Record mit beliebigen Schlüsseln?

interface

Nein, ohne ausdrückliche Indexsignatur nicht. Weil ein interface später über eine Zusammenführung erweitert werden könnte, gilt es nicht als vollständig bekannt.

type

Ja, ein Typalias für eine Objektform bekommt eine implizite Indexsignatur, weil sein Inhalt abschließend feststeht.

Wie verhält sich der Compiler bei sehr vielen zusammengesetzten Typen?

interface

Eine Erweiterung mit extends erzeugt einen flachen, benannten Typ, den der Compiler zwischenspeichern kann.

type

Bei vielen ineinander verschachtelten Zusammenführungen mit dem Und-Zeichen muss mehr neu berechnet werden. Die Projektdokumentation empfiehlt in diesen Fällen ausdrücklich die andere Form.

Was passt wann

Wenn du einen Typ aus einer fremden Bibliothek um eigene Felder ergänzen willst
nimm interface, denn nur damit lassen sich gleichnamige Deklarationen zusammenführen.
Wenn der Typ mehr ist als eine Objektform, also eine Vereinigung, ein Tupel oder etwas Abgeleitetes
nimm type, denn interface kann das schlicht nicht ausdrücken.
Wenn keiner der Sonderfälle zutrifft, was auf die große Mehrheit aller Deklarationen zutrifft
leg eine der beiden Varianten als Konvention fest, schreib einen Satz Begründung dazu und beend damit die Diskussion im Review.

Drei Fragen, und die Schreibweise steht fest

  1. 01 Erweiterst du den Typ einer fremden Bibliothek? Dann brauchst du interface.
  2. 02 Ist der Typ eine Vereinigung, ein Tupel oder abgeleitet? Dann brauchst du type.
  3. 03 Soll die Form zu einer Zuordnung mit offenen Schlüsseln passen? Dann type.
  4. 04 Trifft nichts davon zu? Dann entscheidet die Konvention und nicht der Geschmack.
  5. 05 Gibt es keine Konvention? Dann ist das die eigentliche Aufgabe, nicht die Wahl.
Was du mitnimmst

Was du danach ohne Diskussion entscheidest

Die ehrliche Antwort ist unbequem, weil sie keiner der beiden Seiten recht gibt: Der Unterschied ist kleiner als die Debatte darum, und genau deshalb lohnt es sich, die wenigen Stellen zu kennen, an denen er zählt. Für den Rest zählt nur, dass die Entscheidung getroffen und aufgeschrieben ist.

Modulerweiterungen brauchen interface

Wenn du den Typ einer fremden Bibliothek um ein Feld ergänzen willst, geht das über das Zusammenführen gleichnamiger Deklarationen. Ein type an derselben Stelle meldet einen doppelten Bezeichner, und es gibt keinen Umweg.

Alles jenseits der Objektform braucht type

Vereinigungen, Tupel, benannte Funktionstypen, abgeleitete Typen mit typeof und alles, was aus einem anderen Typ berechnet wird, lässt sich mit interface nicht ausdrücken. Hier gibt es keine Wahl.

Die Zuweisbarkeit an eine offene Zuordnung kennen

Ein type für eine Objektform passt zu einer Zuordnung mit beliebigen Schlüsseln, ein gleich aussehendes interface nicht. Der Grund ist, dass ein interface später noch erweitert werden könnte. Diese Meldung kostet ohne Vorwissen eine halbe Stunde.

Bei Widersprüchen den früheren Fehler bevorzugen

Erweiterst du mit extends und widersprechen sich zwei Felder, meldet der Compiler das an der Deklaration. Bei einer Zusammenführung mit dem Und-Zeichen wird das Feld stillschweigend zu never, und die Meldung kommt erst am Aufrufort.

Eine Konvention statt einer Überzeugung

Für alle übrigen Fälle legst du eine Variante fest, schreibst einen Satz Begründung dazu und lässt es den Linter prüfen. Damit ist die Frage im Review erledigt, unabhängig davon, welche Seite gewonnen hat.

KI-generiertDieses Bild wurde mit KI erzeugt · Yves Hoppe / KI / cmt

Was tatsächlich gleich ist

Beide Schreibweisen beschreiben Objektformen, und der Compiler behandelt das Ergebnis in aller Regel gleich. Beide lassen sich mit Typparametern versehen, beide funktionieren mit implements an einer Klasse, beide erlauben optionale und nur lesbare Felder, beide lassen sich erweitern: interface mit extends, ein Typalias über das Und-Zeichen. Ein interface kann auch von einem Typalias erben, solange dieser eine Objektform beschreibt, und umgekehrt lässt sich ein interface in eine Zusammenführung aufnehmen. Die beiden Welten sind also nicht getrennt, sondern beschreiben in weiten Teilen dasselbe mit anderer Schreibweise.

Auch der oft genannte Unterschied bei der Lesbarkeit von Fehlermeldungen ist kleiner, als er dargestellt wird. Ein benannter Typ erscheint in Meldungen unter seinem Namen, gleich in welcher Form er deklariert wurde. Wirklich unterschiedlich wird es erst bei Zusammenführungen mit dem Und-Zeichen: Die erscheinen in Meldungen häufig ausgeschrieben als Verknüpfung mehrerer Typen, während eine Erweiterung mit extends unter dem Namen des erweiterten Typs bleibt. Wer viele solcher Verknüpfungen hat, merkt das beim Lesen der Ausgabe.

Das Zusammenführen gleichnamiger Deklarationen

Schreibst du zweimal interface Kunde in denselben Bereich, entsteht kein Fehler, sondern ein Typ mit den Feldern aus beiden Deklarationen. Innerhalb einer Datei ist das fast immer ein Versehen und ein guter Grund, es sich abzugewöhnen. Über Modulgrenzen hinweg ist es dagegen das einzige Mittel, um fremde Typdeklarationen zu erweitern. Der übliche Fall: Eine Bibliothek für Server hängt an ihr Anfrageobjekt weitere Felder, sobald bestimmte Zusatzmodule eingebunden sind, und dein Code soll diese Felder kennen. Du schreibst dazu einen declare-module-Block mit einem gleichnamigen interface und deinem zusätzlichen Feld, und ab dann kennt der Compiler es überall.

Mit einem Typalias geht das nicht, dort meldet der Compiler einen doppelten Bezeichner. Es gibt auch keinen Umweg über eine Zusammenführung, weil die Erweiterung ja am ursprünglichen Namen ansetzen muss und nicht an einem neuen. Das ist der eine Fall, in dem eine Teamregel gegen interface tatsächlich blockiert, und er kommt in jedem Projekt vor, das eine Bibliothek für Server oder ein Testwerkzeug typisiert erweitert.

Dieselbe Eigenschaft hat eine unerwartete Nebenwirkung, die den dritten wichtigen Unterschied erklärt. Weil ein interface jederzeit von außen erweitert werden könnte, betrachtet der Compiler seine Feldliste als nicht abschließend. Ein Typalias für dieselbe Form gilt dagegen als vollständig bekannt und bekommt deshalb eine implizite Indexsignatur. Praktisch heißt das: Ein Wert mit einem Typalias lässt sich an einen Parameter vom Typ Record mit beliebigen Schlüsseln übergeben, ein gleich aussehendes interface nicht. Die Meldung dazu nennt weder das Wort interface noch die Erweiterbarkeit, und deshalb kostet sie beim ersten Mal verlässlich eine halbe Stunde.

extends gegen das Und-Zeichen

Beide Wege fügen Felder hinzu, aber sie melden Widersprüche zu verschiedenen Zeitpunkten. Erweiterst du ein interface mit extends und deklarierst dabei ein Feld, das im Ausgangstyp einen anderen Typ hat, meldet der Compiler das sofort an der Deklaration. Führst du zwei Typaliase mit dem Und-Zeichen zusammen, bleibt die Deklaration still: Bei zwei unverträglichen einfachen Typen wird das Feld zu never, bei zwei Objekttypen entsteht eine Verschneidung, die kein Wert mehr erfüllen kann, und bei zwei Funktionstypen eine Überladung aus beiden Signaturen. Auffällig wird das erst dort, wo jemand einen Wert zuweisen will, und die Meldung erscheint dann an einer Stelle, die mit der Ursache nichts zu tun hat.

Der zweite Unterschied betrifft den Compiler selbst. Eine Erweiterung mit extends ergibt einen benannten, flachen Typ, den der Compiler zwischenspeichern und bei jedem weiteren Vergleich wiederverwenden kann. Eine tief verschachtelte Verknüpfung mit dem Und-Zeichen muss dagegen bei jedem Vergleich neu abgearbeitet werden. In kleinen Projekten merkt das niemand. In großen, wo dieselben Verknüpfungen an hunderten Stellen auftauchen, ist das ein spürbarer Anteil an der Dauer eines Prüflaufs, und die Projektdokumentation von TypeScript empfiehlt für diesen Fall ausdrücklich die Erweiterung statt der Verknüpfung.

Eine Teamregel, die trägt

Eine Regel, die in der Praxis funktioniert, hat drei Teile. Erstens: Für Objektformen, die nach außen sichtbar sind und von anderen Modulen benutzt werden, nimmst du eine der beiden Formen durchgehend. Welche, ist zweitrangig, wichtig ist die Einheitlichkeit, denn danach sieht dieselbe Sache im ganzen Projekt gleich aus. Zweitens: Für alles, was keine reine Objektform ist, also Vereinigungen, Tupel, Funktionstypen und alles Abgeleitete, nimmst du type, weil es keine Alternative gibt. Und drittens: Für Erweiterungen fremder Deklarationen nimmst du interface, aus demselben Grund.

Damit die Regel nicht in einem Dokument versauert, das niemand liest, gehört sie in die Linter-Konfiguration. typescript-eslint hat dafür die Regel consistent-type-definitions, die sich auf eine der beiden Formen festlegen lässt und die andere für gewöhnliche Objektformen meldet. Die Sonderfälle stören dabei nicht, weil eine Vereinigung ohnehin kein interface sein kann und deshalb gar nicht geprüft wird.

Was du dir sparen kannst, ist eine Umstellung des Bestands. Der Gewinn einer einheitlichen Schreibweise entsteht beim Lesen neuer Deklarationen, nicht dadurch, dass alte umgeschrieben werden. Ein Massenumbau erzeugt einen großen Änderungssatz ohne fachlichen Inhalt, verdeckt in der Versionsgeschichte alles Übrige und bringt in genau einem Fall sogar echten Ärger: dort, wo ein interface bisher über eine Zusammenführung erweitert wurde und diese Erweiterung nach der Umstellung stillschweigend nicht mehr greift.

Dazu passende Kurse

Warum es zwei Wege, dieselbe Struktur zu beschreiben gibt und wann die Wahl wirklich zählt, gehört zum Stoff der TypeScript-Kurse.

Weil sich diese Frage erst ab einer gewissen Projektgröße bemerkbar macht, passen dazu Kurse rund um den Aufbau größerer Webanwendungen .

Wissen prüfen

Wie sicher bist du beim Thema wirklich?

Lesen fühlt sich schnell nach Können an. Ein kurzer Test zeigt dir, was davon schon sitzt und wo sich ein Kurs lohnt. Kostenlos, ohne Anmeldung, mit einer Erklärung zu jeder Antwort.

Ein sehr gutes, praxisorientiertes und nachhaltiges Seminar. So sollte es immer sein. Vielen Dank.
LimeSurvey - Anwendertraining Teil 1 (Grundlagen)
Die Schulung ist gerade für Einsteiger oder Entwickler*innen mit eingestaubtem Basiswissen sehr hilfreich.
Vue.js 3 Grundkurs
Ich bin sehr glücklich Franz als Trainer gehabt zu haben. Er hat offensichtlich unglaublich viel Wissen zu den Themen, und schafft es, dieses auch interaktiv und verständlich weiterzugeben.
React Komplettausbildung

Häufige Fragen

Gibt es einen Unterschied im erzeugten JavaScript?
Nein, keinen. Beide Schreibweisen verschwinden vollständig bei der Übersetzung, im Ergebnis steht davon keine Zeile. Die Wahl betrifft ausschließlich die Prüfung während der Übersetzung und die Lesbarkeit für Menschen. Alle Argumente, die sich auf die Laufzeit berufen, gehen an der Sache vorbei.
Kann ich ein interface aus einem Typalias erweitern und umgekehrt?
Ja, in beide Richtungen, solange es um Objektformen geht. Ein interface darf mit extends von einem Typalias erben, wenn dieser eine Objektform beschreibt, und ein Typalias darf ein interface in eine Verknüpfung aufnehmen. Was nicht geht, ist ein interface, das von einer Vereinigung erbt, denn eine Vereinigung ist keine einzelne Objektform.
Warum meldet der Compiler bei meinem interface, dass es nicht zu einem Record passt?
Weil ein interface durch das Zusammenführen gleichnamiger Deklarationen jederzeit von außen erweitert werden könnte. Der Compiler betrachtet seine Feldliste deshalb als nicht abschließend und vergibt keine implizite Indexsignatur. Ein Typalias für dieselbe Form gilt als vollständig bekannt und passt. Du kannst die Signatur auch ausdrücklich hinzufügen, aber meistens ist der Wechsel der Schreibweise die kleinere Änderung.
Welche Form nutzen große Bibliotheken?
Beide, und zwar begründet: interface für alles, was Anwender erweitern können sollen, type für Vereinigungen und für Typen, die aus anderen berechnet werden. Genau diese Aufteilung ist die brauchbare Antwort auf die Frage. Wer eine Bibliothek schreibt, sollte nach außen sichtbare Objektformen als interface deklarieren, damit die Nutzer sie ergänzen können.
Persönlich für dich da

Deine Ansprechpartner

Du bist dir nicht sicher, welcher Kurs oder welches Level zu dir passt? Wir beraten dich persönlich und kostenlos.

Yves Hoppe

Yves Hoppe

Weiterbildung & Beratung

Hilft dir, aus dem Webentwicklung-Programm den passenden Kurs für deinen Stand zu finden.

Norbert Jansen

Norbert Jansen

Beratung & Inhouse

Plant mit dir Inhouse-Trainings, die auf eure Abläufe und euren Datenbestand zugeschnitten sind.

Eine Konvention statt einer Dauerdiskussion im Review

In den TypeScript-Kursen bei cmt gehst du beide Schreibweisen an denselben Beispielen durch und siehst dabei, an welchen wenigen Stellen die Wahl tatsächlich etwas ändert.