TypeScript Types & Interfaces

6 Kernkonzepte
Die wichtigsten TypeScript-Konzepte für Typen und Interfaces: type vs interface · Union Types · Intersection Types · Utility Types · Mapped Types · Conditional Types TypeScript bietet eine Vielzahl von Möglichkeiten, Typen zu definieren und zu manipulieren. Diese Konzepte sind essenziell für die Entwicklung typisierter, flexibler und robuster Anwendungen.

type vs interface – Unterschiede & Anwendung

type · interface · extends · implements
interface User { name: string; } type User = { name: string; } // Beide definieren die gleiche Struktur

interface und type sind zwei Wege, Typen in TypeScript zu definieren. Sie haben Überschneidungen, aber auch wichtige Unterschiede in der Anwendung.

Vergleich: type vs interface

Merkmal interface type
Erweiterung extends (mehrfach) Intersection (&)
Implementierung class implements Nicht möglich
Deklarations-Merging Ja (automatisch) Nein
Union / Intersection Nein (nur als &) Ja (|, &)
Primitive Typen Nein Ja (type ID = string)
Mapped Types Nein Ja
Beispiele
// Interface: Erweiterung mit extends
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }
// Type: Union & Intersection
type ID = string | number;
type Employee = Person & { salary: number };
// Deklarations-Merging (nur interface)
interface Config { port: number; }
interface Config { host: string; } // Automatisch zusammengeführt
// Class implements interface
class MyUser implements User { /* ... */ }
Tipp: Verwenden Sie interface für Objektstrukturen, die erweitert oder von Klassen implementiert werden sollen. Verwenden Sie type für Union Types, Intersection Types, primitive Typen und Mapped Types.

Union Types – Entweder/oder-Typen

| · type guard · discriminated union
type Status = "pending" | "done" | "failed"; type Value = string | number | boolean;

Union Types erlauben einem Wert, einer von mehreren Typen zu sein. Sie werden mit dem Pipe-Operator (|) erstellt und erfordern oft Typ-Prüfungen (type guards).

Beispiele
// Einfache Union
type Response = string | { data: unknown; error: string };
// Discriminated Union (getaggte Union)
interface Circle { kind: "circle"; radius: number; }
interface Square { kind: "square"; sideLength: number; }
type Shape = Circle | Square;
// Type Guard
function getArea(shape: Shape): number {
if (shape.kind === "circle") {
return Math.PI * shape.radius ** 2;
} else {
return shape.sideLength ** 2;
}
}
// Verwendung
const circle: Shape = { kind: "circle", radius: 5 };
const square: Shape = { kind: "square", sideLength: 4 };
console.log(getArea(circle)); // 78.54
Tipp: Discriminated Unions (mit einer gemeinsamen kind-Eigenschaft) sind ein mächtiges Muster für die Modellierung von Zuständen und die Typsicherheit in TypeScript.

Intersection Types – Typen kombinieren

& · type · interface
type Person = { name: string; age: number; }; type Employee = Person & { employeeId: number; }; type FullUser = Person & Employee & { department: string; };

Intersection Types kombinieren mehrere Typen zu einem einzigen Typ, der alle Eigenschaften der kombinierten Typen enthält. Sie werden mit dem &-Operator erstellt.

Beispiele
// Grundlegende Intersection
interface HasName { name: string; }
interface HasAge { age: number; }
type Person = HasName & HasAge;
// Verwendung
const user: Person = { name: "Anna", age: 30 };
// Mehrere Typen kombinieren
interface Address { street: string; city: string; }
type UserWithAddress = Person & Address;
// Intersection mit Union (Fortgeschritten)
type Result = { data: unknown } & { status: "success" | "error" };
Tipp: Intersection Types sind besonders nützlich, um Typen aus mehreren Quellen zusammenzuführen – z.B. für das Kombinieren von API-Rückgabetypen oder die Erweiterung von Konfigurationen.

Utility Types – Hilfstypen

Partial · Pick · Omit · Record · Required · Readonly
type PartialUser = Partial<User>; type UserNames = Pick<User, "firstName" | "lastName">; type UserWithoutId = Omit<User, "id">; type UserMap = Record<string, User>;

Utility Types sind eingebaute generische Typen, die häufige Typ-Transformationen durchführen – wie das optional machen von Eigenschaften (Partial) oder das Auswählen bestimmter Schlüssel (Pick).

Die wichtigsten Utility Types

Utility Beschreibung Beispiel
Partial<T> Macht alle Eigenschaften optional { id?: number; name?: string }
Required<T> Macht alle Eigenschaften erforderlich { id: number; name: string }
Readonly<T> Macht alle Eigenschaften schreibgeschützt { readonly id: number; readonly name: string }
Pick<T, K> Wählt nur bestimmte Schlüssel aus { name: string }
Omit<T, K> Entfernt bestimmte Schlüssel { name: string }
Record<K, T> Erstellt einen Objekttyp mit Schlüsseln K { [key: string]: User }
Exclude<T, U> Entfernt Typen aus einer Union string | number
Extract<T, U> Extrahiert Typen aus einer Union string
NonNullable<T> Entfernt null und undefined string | number
Beispiele
interface User { id: number; name: string; email: string; }
// Alle Eigenschaften optional
type UserUpdate = Partial<User>;
// Nur Name und Email
type UserContact = Pick<User, "name" | "email">;
// Alle außer ID
type UserWithoutId = Omit<User, "id">;
// Record: Dictionary von Users
type UserDictionary = Record<string, User>;
const users: UserDictionary = {
user1: { id: 1, name: "Anna", email: "anna@example.com" },
user2: { id: 2, name: "Ben", email: "ben@example.com" }
};
Tipp: Utility Types sind besonders nützlich in generischen Funktionen und bei der Arbeit mit APIs, wo Sie Teile von Objekten aktualisieren oder filtern möchten.

Mapped Types – Typen transformieren

keyof · in · as · readonly · ?
type ReadonlyKeys<T> = { readonly [P in keyof T]: T[P]; }; type OptionalKeys<T> = { [P in keyof T]?: T[P]; };

Mapped Types durchlaufen alle Eigenschaften eines Typs und transformieren sie – z.B. um sie optional, schreibgeschützt oder umzubenennen. Sie sind die Grundlage vieler Utility Types.

Beispiele
// Alle Eigenschaften zu nullbaren Werten machen
type NullableKeys<T> = {
[P in keyof T]: T[P] | null;
};
// Nur bestimmte Typen transformieren (string -> number)
type StringToNumber<T> = {
[P in keyof T]: T[P] extends string ? number : T[P];
};
// Eigenschaften umbenennen (mit as)
type RenameKeys<T> = {
[P in keyof T as `new_${P & string}`]: T[P];
};
// Verwendung
interface Config { port: number; host: string; }
type NullableConfig = NullableKeys<Config>;
// { port: number | null; host: string | null }
Tipp: Mapped Types sind ein fortgeschrittenes Konzept, das häufig in Bibliotheken und Frameworks verwendet wird. Sie sind die Grundlage für viele der eingebauten Utility Types.

Conditional Types – Typen abhängig von Bedingungen

extends · infer · ? :
type IsString<T> = T extends string ? "Ja" : "Nein"; type Result1 = IsString<string>; // "Ja" type Result2 = IsString<number>; // "Nein"

Conditional Types wählen einen Typ basierend auf einer Bedingung aus – ähnlich wie ein ternärer Operator (? :) auf Typebene. Sie ermöglichen flexible und dynamische Typdefinitionen.

Beispiele
// Grundlegender Conditional Type
type TypeName<T> =
T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
"unknown";
// Mit infer (Typ extrahieren)
type ArrayElement<T> = T extends (infer U)[] ? U : never;
type Element = ArrayElement<number[]>; // number
// ReturnType (extrahieren des Rückgabetyps)
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
const fn = (x: number) => x + 1;
type FnReturn = MyReturnType<typeof fn>; // number
// Verschachtelte Conditional Types
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
Tipp: infer ist besonders nützlich, um Typen aus komplexen Strukturen zu extrahieren – z.B. den Rückgabetyp einer Funktion oder den Elementtyp eines Arrays.

Types & Interfaces im Überblick

type Type Aliases
Union, Intersection, Primitive
interface Strukturen
extends, implements, Merging
| Union Types
Entweder/oder
& Intersection Types
Kombination
Utility Partial, Pick, Omit, Record
Hilfstypen
infer Typ extrahieren
Conditional Types

Quick Summary

type
Type Alias
|
Union Types
&
Intersection Types
Partial
Utility Types
keyof
Mapped Types
extends ?
Conditional Types
type ID = string | number · interface User extends Person { role: string } · type UserUpdate = Partial<User>