Type vs. Interface

Die Verwendung von TypeScript in der modernen Softwareentwicklungsbranche nimmt mit dem Aufkommen des Konzepts der statischen Typüberprüfung in JavaScript rasch zu.

Die Chancen stehen also gut, dass Du schon einmal von Type und Interface in TypeScript gehört hast.

Gerade Anfängern kann es jedoch passieren, dass sie bei der Arbeit mit Types und Interfaces Fehler machen, da sie nicht mit den „Best Practices“ vertraut sind.

In diesem Artikel möchte ich Dir den Unterschied zwischen Interfaces und Types in TypeScript aufzeigen, anhand von Beispielen und geeigneten Anwendungsfällen, die Dir dabei helfen, das Beste aus ihnen herauszuholen.

Interfaces

Interfaces legen die Syntax fest, die jede Entität befolgen muss.

Ein Interface definiert die Mitglieder einer Schnittstelle, d. h. ihre Eigenschaften, Methoden und Ereignisse.

Nur die Deklarationen der Mitglieder sind in einem Interface enthalten. Die abgeleitete Klasse ist für die Definition der Mitglieder zuständig.

Der TypeScript-Compiler verwendet Interfaces zur Typüberprüfung und hilft häufig bei der Festlegung einer Standardstruktur, an die sich die abgeleiteten Klassen halten. Wir verwenden das Schlüsselwort interface, um Schnittstellen zu erstellen.

interface Person {
  name: string;
  age: number;
}

const john: Person = {
  name: "John",
  age: 26,
}

Types und Type-Aliase

In TypeScript wird ein Datentyp durch seinen Type definiert. Es gibt zahlreiche grundlegende Types, darunter String, Boolean und Number.

Darüber hinaus verfügt TypeScript über fortgeschrittene Typen, die als Type-Aliase bekannte Funktion enthalten.

Mit Type-Aliase können wir den Namen eines Types ändern, ohne einen neuen Type zu definieren. Wir verwenden also das Schlüsselwort type, um einen neuen Type-Alias zu erstellen.

type Person = {
  name: string;
  age: number;
}

const john: Person = {
  name: "John",
  age: 26,
}

Unterschied zwischen Types und Interfaces

Obwohl Types und Interfaces auf den ersten Blick ähnlich aussehen, gibt es viele Unterschiede zwischen ihnen.

Zum Beispiel sind Interfaces im Wesentlichen ein Mittel, um die Formen von Daten zu beschreiben, wie ein Objekt.

Types hingegen sind Definitionen von Datentypen, wie Primitive, Intersection, Union, Tupel oder verschiedene Typen. In diesem Abschnitt werden wir einige wichtige Unterschiede zwischen Interfaces und Types in TypeScript diskutieren.

Kompositions-Überladungen

Wenn zwei Interfaces mit demselben Namen deklariert werden, können diese beiden Interfaces überladen werden.

interface Person {
  name: string
}

interface Person {
  age: number
}

const john: Person = {
  name: "John",
  age: 26,
}

Der TypeScript-Compiler erzeugt einen Fehler, wenn das Schlüsselwort type verwendet wird, um zwei verschiedene Typen mit denselben Variablennamen zu deklarieren.

type Person = {
  name: string
}

// Dies gibt einen Duplicate identifier 'Person' Fehler aus.
type Person = {
  age: number
}

Extends und Intersection

Die Interfaces können mit type oder durch einen type-Alias erweitert werden. Eine der besten Eigenschaften der TypeScript-Interface ist ihre Einfachheit bei der Erweiterung von Klassen.

interface PersonNameInterface { name: string }
interface Person1 extends PersonNameInterface { age: number }

type PersonNameType = { name: string }
interface Person2 extends PersonNameType { age: number }

class PersonClass { name = "John" }
interface Person3 extends PersonClass { age: number }

Da Types, oder Type-Aliase, nicht erweitert werden können und diese Funktionalität nicht unterstützen, kann ein Type keine Klasse erweitern.

Es ist jedoch möglich, mehrere Types zu einem Type zu kombinieren, um einen Intersection-Type zu bilden. Mit dem Schlüsselwort & können wir auch zwei Interfaces kombinieren, um einen neuen Intersection-Type zu erstellen.

Dabei ist jedoch zu beachten, dass die Kombination von zwei Interfaces keine Intersection-Interfaces bilden kann.

type PersonNameType = { name: string; }
type Person1 = PersonNameType & { age: number; }

interface PersonNameInterface { name: string; }
type Person2 = PersonNameInterface & { age: number; }

// Wir können zwei Schnittstellen kombinieren, um einen Intersection-Type zu erstellen, aber keine Intersection-Interfaces erstellen

interface PersonNameInterface { name: string; }
interface PersonAgeInterface { age: number; }
type Person3 = PersonNameInterface & PersonAgeInterface

Implements und Union

Eine Klasse kann ein Interface oder einen Type-Alias auf dieselbe Weise implementieren. Aber denke daran, dass eine Klasse und ein Interface statische Entwürfe sind.

Folglich kannst Du keine Type-Aliase implementieren oder erweitern, die sich auf Union-Types beziehen.

interface PersonInterface {
 name: string;
 age: number;
}

class John implements PersonInterface {
 name = "John";
 age = 26;
}

type PersonType = {
 name: string;
 age: number;
};

class Ann implements PersonType {
 name = "Ann";
 age = 26;
}

type UnionType = { name: string; } | { age: number; };

// Gibt ein Fehler aus
class Joel implements UnionType {
 name = "Joel";
 age = 2;
}

Ein TypeScript-Type unterstützt keine Implements. Wir können keine Type-Aliase mit Implements erstellen, die zur Definition von Variablen verwendet werden können. Aber wir können Union-Types erstellen.

Durch die Kombination von zwei Interfaces oder Types können wir einen neuen Union-Type erstellen, der einen oder mehrere Types mit dem Schlüsselwort | enthalten kann.

type PersonNameType = {
 name: string
};

type PersoneAgeType = {
 age: number
};

type Person1 = PersonNameType | PersoneAgeType;

interface PersonNameInterface {
 name: string
};

interface PersoneAgeInterface {
 age: number
};

type Person2 = PersonNameInterface | PersoneAgeInterface;

Andere Typen

Der Type-Alias kann, anders als ein Interface, für zusätzliche Typen wie Primitive, Unions und Tupel verwendet werden.

// primitive
type Name = string;

// object
type PersonName = { name: string; };
type PersonAge = { y: number; };

// union
type PartialPoint = PersonName | PersonAge;

// tuple
type Data = [number, string];

In TypeScript können wir nur Types verwenden, um die oben erwähnten „andere Typen“ anzugeben, nicht aber Interfaces.

Allerdings können wir diese Types auch innerhalb eines Interfaces verwenden, wie im folgenden Beispiel:

interface Person {
  name: string
  obj: {}
  union: string | number
  tuple: [string, number]
}

Wann wird was genutzt?

Ein Interface verwendet man, um:

  • ein neues Objekt oder eine Objektmethode definieren zu können.
  • von der Zusammenführung von Kompositions-Überladungen profitieren zu können.

Types werden verwendet, wenn:

  • wir einen Alias für einen primitiven Typen definieren wollen.
  • wir eine Definition von Tupel-Type erstellen wollen.
  • wir einen Union-Type definieren möchten.
  • wir eine Funktion erstellen und diese in ein Objekttyp durch Kompositions Überladung definiert werden soll.
  • wir Union-Types erstellen wollen.

Letztendlich solltest Du die Situation sorgfältig abwägen und bewerten, bevor Du dich entscheidest, ob Du einen Type oder ein Interface verwendest.

Types sind besser geeignet für die Verwendung von Funktionen, komplexen Typen usw.

Interfaces funktionieren besser mit Objekten und Methodenobjekten.

Zusammenfassung

In diesem Artikel haben wir TypeScript-Types und -Interfaces besprochen. Wir dürfen nicht vergessen, dass es keinen klaren Gewinner gibt, da sie nahezu identisch sind.

Da sie sich in bestimmten Aspekten unterscheiden, müssen wir die Anwendungsfälle sorgfältig abwägen, um zu entscheiden, welche verwendet werden sollte. Die Entscheidung für das eine oder das andere liegt jedoch letztlich beim Entwickler, also dir.