TypeScript-Schnittstelle vs. Typ: Echte Unterschiede, getestet mit dem Compiler

  1. Startseite
  2. Blog
  3. Softwareentwicklung

Softwareentwicklung

TypeScript-Schnittstelle vs. Typ: Echte Unterschiede, getestet mit dem Compiler

Schnittstellen und Typaliase überschneiden sich so stark, dass die Auswahl oft willkürlich wirkt. Hier erfahren Sie, was jeder tun kann, was der andere nicht kann, was durch echte Compilerfehler bestätigt wird, sowie eine einfache Regel, die Ihr Team befolgen kann.

8 Min. Lesezeit
TypeScript-Schnittstelle vs. Typ: echte Unterschiede, getestet mit dem Compiler

Das Wichtigste

  • Für einfache Objektformen sind eine Schnittstelle und ein Typ-Alias nahezu austauschbar.
  • Nur type kann Unions, Tupel und andere Nicht-Objekttypen benennen.
  • Nur interface unterstützt die Deklarationszusammenführung, bei der zwei Deklarationen kombiniert werden.
  • Eine allgemeine Regel: Schnittstellen für Objektformen, type für alles andere, konsequent angewendet.

Nur wenige TypeScript-Fragen sorgen für so viele Debatten wie TypeScript-Schnittstelle vs. Typ. In vielen Artikeln werden Unterschiede aufgeführt, die in älteren Versionen eine Rolle spielten, jetzt aber veraltet sind. Anstatt also Behauptungen zu wiederholen, haben wir sie getestet.

Wir haben eine Datei geschrieben, die jeden Unterschied ausübt, und sie mit TypeScript 5.6.3 im strikten Modus überprüft. Die unten aufgeführten Compilerfehler stammen aus diesem Lauf. Das Verhalten kann sich zwischen den Versionen ändern. Vergleichen Sie es daher mit der TypeScript-Version, die Ihr Projekt verwendet. Wenn Sie neu in der Sprache sind, beginnen Sie mit unserem TypeScript-Tutorial für Anfänger.

Die kurze Antwort

Eine interface beschreibt die Form eines Objekts und kann erweitert oder erneut geöffnet werden. Ein type-Alias ​​gibt einem jedem Typ einen Namen, einschließlich Objekten, Unions, Tupeln und Funktionstypen. Wo beides funktionieren kann, liegt der Unterschied hauptsächlich im Stil.

Wo sie gleich sind

Diese beiden Deklarationen beschreiben identische Formen:

interface User {
  id: number;
  name: string;
  email?: string;
}

type UserAlias = {
  id: number;
  name: string;
  email?: string;
};

const a: User = { id: 1, name: "Asha" };
const b: UserAlias = a;   // no error

TypeScript verwendet strukturelle Typisierung, was bedeutet, dass es Formen statt Namen vergleicht. Deshalb kann eine User ohne Beanstandung einer UserAlias zugeordnet werden. Beide unterstützen optionale Eigenschaften, readonly Eigenschaften, Methoden und Indexsignaturen.

Schnittstelle vs. Typ nebeneinander

Funktion Schnittstelle Typ
Objektformen Ja Ja
Gewerkschaften wie "a" | "b" Nein Ja
Tupel und Grundelemente Nein Ja
Zugeordnete und bedingte Typen Nein Ja
Deklarationszusammenführung Ja Nein, es ist ein Fehler
Erweitern erstreckt sich & Kreuzung
Widersprüchliche Eigenschaften Fehler bei der Deklaration Kann ruhig zu never werden
Verwendet mit implements Ja Ja, wenn es sich um einen Objekttyp handelt

Was nur Typ kann

Viele nützliche Typen sind überhaupt keine Objektformen und können nur mit einem Typalias benannt werden:

type Status = "pending" | "paid" | "shipped";   // union
type Point = [number, number];                    // tuple
type Handler = (event: string) => void;           // function type
type Keys = keyof User;                           // computed type
type ReadonlyUser = Readonly<User>;               // utility type

Union-Typen gehören zu den wertvollsten Funktionen von TypeScript, da der Compiler dann jeden Wert außerhalb der Liste ablehnt:

let s: Status = "paid";
s = "refunded";

// error TS2322: Type '"refunded"' is not assignable to type 'Status'.

Dadurch werden Tippfehler und veraltete Werte zur Kompilierungszeit erkannt, bevor sie in die Produktion gelangen.

Deklarationszusammenführung

Wenn Sie eine Schnittstelle mit demselben Namen zweimal deklarieren, führt TypeScript sie zu einer zusammen:

interface Settings {
  theme: string;
}
interface Settings {
  language: string;
}

const settings: Settings = { theme: "dark", language: "en" };   // no error

Versuchen Sie dasselbe mit einem Typalias, aber der Compiler weigert sich:

type Config = { theme: string };
type Config = { language: string };

// error TS2300: Duplicate identifier 'Config'.

Durch das Zusammenführen können Sie mit Bibliotheken ihre Typen erweitern. Beispielsweise können Sie eine benutzerdefinierte Eigenschaft zur globalen Window-Schnittstelle oder zum Anforderungsobjekt eines Frameworks hinzufügen, ohne die Bibliothek zu ändern. Das ist wirklich nützlich.

Die Kehrseite ist, dass eine Zusammenführung zufällig erfolgen kann. Wenn zwei Dateien in einem Projekt eine Schnittstelle mit demselben Namen im selben Bereich deklarieren, werden sie stillschweigend kombiniert, was verwirrend sein kann. Ein Typalias macht diese Kollision stattdessen zu einem Fehler.

erstreckt sich vs. Kreuzungen

Mit beiden können Sie auf einem vorhandenen Typ aufbauen:

interface Admin extends User {
  permissions: string[];
}

type AdminAlias = UserAlias & { permissions: string[] };

Bei kompatiblen Eigenschaften ist das Ergebnis dasselbe. Der Unterschied zeigt sich in einem Konflikt. Wenn eine Schnittstelle versucht, den Typ einer Eigenschaft inkompatibel zu ändern, meldet der Compiler dies sofort:

interface Base { id: number }
interface Broken extends Base { id: string }

// error TS2430: Interface 'Broken' incorrectly extends interface 'Base'.
//   Types of property 'id' are incompatible.
//     Type 'string' is not assignable to type 'number'.

Eine Kreuzung beschwert sich nicht, wenn sie deklariert wird. Stattdessen wird die widersprüchliche Eigenschaft zu never, einem Typ, den kein Wert haben kann. Der Fehler tritt erst später auf, wenn Sie versuchen, ihn zu verwenden:

type Mixed = { id: number } & { id: string };
const m: Mixed = { id: 1 };

// error TS2322: Type 'number' is not assignable to type 'never'.

Dieser spätere Fehler ist verwirrender, da er vom eigentlichen Fehler abweicht. Dies ist ein praktischer Grund, extends beim Aufbau von Objekttypen zu bevorzugen.

Klassen und Implementierungen

Eine Klasse kann mithilfe von implements versprechen, einer Form zu entsprechen, und dies funktioniert mit einer Schnittstelle oder einem Objekttyp-Alias:

class Account implements User {
  constructor(public id: number, public name: string) {}
}

Dies wurde ohne Fehler kompiliert. Beachten Sie, dass implements nur prüft, ob die Klasse übereinstimmt. Es werden keine Eigenschaften oder Code hinzugefügt. Eine Klasse kann keinen Union-Typ implementieren, da es keine einzelne passende Form gibt.

Schreibgeschützte und Dienstprogrammtypen

Dienstprogrammtypen wie Readonly, Partial, Pick und Omit funktionieren sowohl mit Schnittstellen als auch mit Aliasen. Das Ergebnis ist ein neuer Typ, der normalerweise mit einem Typalias benannt wird:

const r: ReadonlyUser = { id: 3, name: "Dev" };
r.name = "Chitra";

// error TS2540: Cannot assign to 'name' because it is a read-only property.

Denken Sie daran, dass readonly nur eine Überprüfung zur Kompilierungszeit ist. Zur Laufzeit handelt es sich bei dem Objekt um normales JavaScript und es kann dennoch durch Code geändert werden, der die Typen umgeht.

Tipp: Typen verschwinden, wenn TypeScript zu JavaScript kompiliert wird. Daten, die von außen eintreffen, beispielsweise eine API-Antwort, werden zur Laufzeit nicht überprüft, nur weil sie einen Typ haben. Validieren Sie es, beispielsweise mit einer Schemabibliothek. Unser Tutorial zum Abrufen der API behandelt den sicheren Umgang mit API-Antworten.

Compiler-Leistung

Die eigenen Leistungsrichtlinien des TypeScript-Teams schlagen vor, Schnittstellen, die extends verwenden, gegenüber großen Schnittmengen zu bevorzugen, da der Compiler Schnittstellenbeziehungen effizienter zwischenspeichern kann. Bei kleinen und mittleren Projekten werden Sie wahrscheinlich keinen Unterschied bemerken. Dies kann in sehr großen Codebasen mit tief zusammengesetzten Typen von Bedeutung sein. Wir haben dies nicht selbst gemessen.

Welches sollten Sie verwenden?

Eine weit verbreitete Regel, die den Leitlinien des TypeScript-Handbuchs entspricht, lautet:

  • Verwenden Sie interface für Objektformen, insbesondere öffentliche APIs und alles, was Klassen implementieren oder andere erweitern können.
  • Verwenden Sie type für Vereinigungen, Tupel, Funktionstypen und zugeordnete oder berechnete Typen.
Du definierst Benutzen
Ein Datenmodell, z. B. Benutzer oder Bestellung Schnittstelle
Eine Reihe zulässiger Werte type Gewerkschaft
Ein Paar oder Tripel mit fester Länge type Tupel
Eine Rückrufsignatur Typ
Erweitern der Typen einer Bibliothek interface, durch Zusammenführung
Eine Variation eines vorhandenen Typs type mit einem Dienstprogrammtyp

Manche Teams verwenden einfach überall type, und das funktioniert auch gut. Am wichtigsten ist die Konsistenz. Wählen Sie also eine Konvention, schreiben Sie sie in Ihren Styleguide und lassen Sie sie von einem Linter durchsetzen. Die consistent-type-definitions-Regel von ESLint aus typescript-eslint kann dies tun.

Häufige Fehler

  • Endlose Debatte. Beides ist für Objekte in Ordnung, also klären Sie die Regel einmal und machen Sie weiter.
  • Versehentliches Zusammenführen. Die Wiederverwendung eines Schnittstellennamens im selben Bereich führt diese stillschweigend zusammen.
  • Widersprüchliche Schnittmengen. Sie erstellen später never Typen mit verwirrenden Fehlern.
  • Typen zur Laufzeit vertrauen. Sie werden gelöscht, also validieren Sie externe Daten.
  • Verwendung von any zum Stummschalten von Fehlern. Die Überprüfung wird ausgeschaltet. Bevorzugen Sie unknown und grenzen Sie es ein.

Wechseln zwischen ihnen in einer vorhandenen Codebasis

Das Ändern einer interface in eine type oder umgekehrt ist für einfache Objektformen normalerweise sicher, da TypeScript Typen anhand ihrer Struktur und nicht anhand ihrer Namen vergleicht. Vor dem Wechsel müssen noch einige Dinge überprüft werden:

  • Durch das Konvertieren einer Schnittstelle in einen Typalias wird die Deklarationszusammenführung aufgehoben, sodass jede zweite Deklaration mit demselben Namen zu einem Fehler wegen doppelter Bezeichner wird.
  • Das Konvertieren eines Typalias in eine Schnittstelle funktioniert nur für Objektformen. Unions, Tupel und zugeordnete Typen müssen als Typaliase bleiben.
  • Fehlermeldungen und Editor-Hover zeigen den Typ möglicherweise anders an, was Teamkollegen überraschen kann, aber das Verhalten nicht ändert.

Nehmen Sie die Änderung in einem fokussierten Pull-Request vor, führen Sie den Compiler im strikten Modus und Ihre Testsuite aus und vermeiden Sie eine Vermischung mit Feature-Arbeit, damit Prüfer sehen können, dass sich sonst nichts geändert hat.

Fazit

Auf die Frage zwischen Schnittstelle und Typ gibt es eine klare Antwort: Sie überschneiden sich bei Objekten, jedes hat ein paar einzigartige Fähigkeiten und Konsistenz ist wichtiger als die Auswahl. Verwenden Sie für alles andere Schnittstellen für Formen und Typen, sofern Ihr Team nichts anderes bevorzugt. Um die Unterschiede selbst zu sehen, kopieren Sie die obigen Beispiele in eine Datei und führen Sie tsc --noEmit --strict darauf aus.

Häufige Fragen

Was ist der Unterschied zwischen Schnittstelle und Typ in TypeScript?

Beide können Objektformen beschreiben. Nur der Typ kann Unions, Tupel und berechnete Typen benennen, während nur die Schnittstelle das Zusammenführen von Deklarationen unterstützt.

Sollte ich eine Schnittstelle oder einen Typ für React-Requisiten verwenden?

Beides funktioniert gut. Viele Teams verwenden Schnittstellen für Requisiten, während andere Schriftarten aus Gründen der Konsistenz mit Gewerkschaften bevorzugen. Wählen Sie eines aus und wenden Sie es im gesamten Projekt an.

Kann ein Typ eine Schnittstelle erweitern?

Ja, indem Sie eine Schnittmenge wie den Typ „Admin = Benutzer & { Berechtigungen: Zeichenfolge[]} verwenden. Eine Schnittstelle kann auch einen Objekttyp-Alias ​​erweitern.

Ist die Schnittstelle schneller als der Typ?

Die Leistungsrichtlinien von TypeScript bevorzugen Schnittstellenerweiterungen über große Schnittmengen in großen Codebasen. Bei typischen Projekten dürfte der Unterschied kaum spürbar sein.

Was ist die Zusammenführung von Deklarationen?

Dies ist der Fall, wenn zwei Schnittstellen mit demselben Namen im selben Bereich zu einer kombiniert werden. Typaliase können nicht zusammengeführt werden und doppelte Namen verursachen einen Fehler.

Lerne weiter

Arbeiten Sie mit Stack Quora

Benötigen Sie Hilfe beim Testen, WordPress oder SEO?

Wir helfen Teams, bessere Software zu liefern und ihre Websites zu erweitern. Sagen Sie uns, woran Sie gerade arbeiten, und wir schlagen Ihnen einen klaren nächsten Schritt vor.

Lesen Sie weiter

Weitere Anleitungen finden Sie unter Tests, KI und moderne Entwicklung

Hat Ihnen dieser Artikel gefallen? Wählen Sie ein Thema und springen Sie direkt in unsere neuesten Tutorials.

Künstliche Intelligenz

KI-Halluzinationen erklärt: Warum Sprachmodelle Dinge erfinden und wie man sie reduziert

KI-Halluzinationen erklärt: Was sie sind, warum Sprachmodelle sie produzieren, gängige Typen, wie man sie mit Erdung und Eingabeaufforderungen reduz…

Artikel lesen →
DevOps

Erklärte Umgebungsvariablen: .env-Dateien, Geheimnisse und Konfiguration richtig gemacht

Umgebungsvariablen anhand von Python-Beispielen erklärt, die wir ausgeführt haben: Sie lesen und festlegen, warum jeder Wert ein String ist, .env-Da…

Artikel lesen →
Softwareentwicklung

API-Ratenbegrenzung erklärt: Algorithmen, Header und Behandlung von 429-Fehlern

API-Ratenbegrenzung anhand einer von uns durchgeführten Simulation erklärt: Token-Bucket, festes Fenster, gleitendes Fenster, 429 Antworten, Ratenbe…

Artikel lesen →
Testautomatisierung

Spott in Python: unittest.mock, Patch und die Fehler, die Tests zum Lügen machen

Mocking in Python mit unittest.mock, getestet: Mock und return_value, side_effect, Patchen, wo ein Name verwendet wird, Spezifikation und Autospec, …

Artikel lesen →
Softwaretests

Code-Abdeckung erklärt: Leitungs- und Zweigstellenabdeckung und was 100 % wirklich bedeutet

Erklärung der Codeabdeckung anhand eines echten Coverage.py-Laufs: Zeilen-, Zweig- und Bedingungsabdeckung, Lesen eines Berichts, warum bei 100 % Ab…

Artikel lesen →
Datenbanken & SQL

DELETE vs. TRUNCATE vs. DROP in SQL: Unterschiede, Rollback und Verwendungszweck

DELETE vs. TRUNCATE vs. DROP erklärt mit getestetem SQL: was jeweils entfernt wird, Rollback, Trigger, Identitätsrücksetzung, Geschwindigkeit und wi…

Artikel lesen →
Datenbanken & SQL

Erklärte SQL-Ansichten: Erstellen, nutzen und vermeiden Sie Fallstricke mit Beispielen

SQL-Ansichten anhand der von uns ausgeführten Abfragen erklärt: Erstellen einer Ansicht, Live-Ergebnisse, Ausblenden von Spalten, Leistung, aktualis…

Artikel lesen →
Softwareentwicklung

TypeScript-Schnittstelle vs. Typ: Echte Unterschiede, getestet mit dem Compiler

TypeScript-Schnittstelle vs. Typ, überprüft mit dem echten Compiler: Was beide können, was nur einer kann, Deklarationszusammenführung, Erweiterunge…

Artikel lesen →
Softwareentwicklung

Python *args und **kwargs erklärt: Flexible Funktionen mit klaren Beispielen

Python *args und **kwargs erklärt mit Code, den wir ausgeführt haben: Packen und Entpacken, Parameterreihenfolge, Nur-Schlüsselwort- und Nur-Positio…

Artikel lesen →
Softwareentwicklung

Python-Lambda-Funktionen: Syntax, tatsächliche Verwendung und wann man sie nicht verwendet

Python-Lambda-Funktionen erklärt mit Code, den wir ausgeführt haben: Syntax, Sortierung mit Schlüssel, Karte und Filter, Abschlüsse, die Late-Bindin…

Artikel lesen →
Künstliche Intelligenz

Was sind KI-Agenten? Wie sie funktionieren, mit einer einfachen Agentenschleife, die Sie ausführen können

Was sind KI-Agenten? Eine einfache Anleitung zur Agentenschleife, zu Tools, Speicher und Leitplanken, mit einer kleinen Python-Schleife, die wir aus…

Artikel lesen →
DevOps

Überwachung vs. Beobachtbarkeit: Der wahre Unterschied, mit Protokollen, Metriken und Traces

Überwachung vs. Beobachtbarkeit erklärt: bekannte und unbekannte Fehler, Protokolle, Metriken und Spuren, eine exemplarische Vorgehensweise für Chec…

Artikel lesen →
DevOps

Docker-Volumes vs. Bind Mounts: Containerdaten auf die richtige Art und Weise beibehalten

Docker-Volumes vs. Bind-Mounts erklärt: Warum Containerdaten verschwinden, benannte Volumes, Bind-Mounts, tmpfs, Compose-Beispiele, Backups, Berecht…

Artikel lesen →
Testautomatisierung

TDD vs. BDD: Hauptunterschiede, eine Funktion, die auf beide Arten entwickelt wurde und wann sie jeweils verwendet werden sollte

TDD vs. BDD erklärt mit einer Rabattfunktion, die in beide Richtungen aufgebaut ist: Rot-Grün-Refaktor mit Code, den wir ausgeführt haben, Gegeben-W…

Artikel lesen →
Softwareentwicklung

Git merge vs rebase: Unterschiede, Beispiele und wann man was nutzt

Git merge vs rebase erklärt mit echter Log-Ausgabe, einer Vergleichstabelle, der goldenen Regel des Rebase und einem sicheren Team-Workflow.

Artikel lesen →
Softwareentwicklung

Python List Comprehension: Syntax, Beispiele und wann man darauf verzichtet

Python List Comprehension erklärt: Syntax, Filter, if-else, verschachtelte Schleifen, gemessene Zeiten und Fälle, in denen eine normale for-Schleife…

Artikel lesen →
Datenbanken & SQL

SQL Joins erklärt mit Beispielen (Inner, Left, Right, Full)

SQL Joins Schritt für Schritt mit Beispieltabellen und Abfragen verstehen: INNER, LEFT, RIGHT, FULL, CROSS und SELF, plus typische Fehler und Perfor…

Artikel lesen →