Union & Intersection Types
Lerne wie du mit Union und Intersection Types flexible und präzise Typen in TypeScript erstellst.
Aktualisiert:
Union & Intersection Types
Union und Intersection Types sind mächtige Features, die es dir ermöglichen, Typen zu kombinieren. Sie sind fundamental für flexibles und gleichzeitig typsicheres Programmieren.
Union Types
Ein Union Type bedeutet “entweder A ODER B”:
// String oder Number
let id: string | number;
id = "abc123"; // OK
id = 42; // OK
id = true; // Fehler!
Praktische Verwendung
// Funktion, die verschiedene Typen akzeptiert
function formatId(id: string | number): string {
if (typeof id === "string") {
return id.toUpperCase();
}
return `ID-${id}`;
}
console.log(formatId("abc")); // "ABC"
console.log(formatId(123)); // "ID-123"
Union mit Literal Types
Besonders mächtig mit Literal Types:
type Status = "pending" | "approved" | "rejected";
let orderStatus: Status;
orderStatus = "pending"; // OK
orderStatus = "shipped"; // Fehler!
// Funktion mit Union Literal
function handleStatus(status: Status): string {
switch (status) {
case "pending":
return "In Bearbeitung...";
case "approved":
return "Genehmigt!";
case "rejected":
return "Abgelehnt.";
}
}
Discriminated Unions
Ein Pattern für Objekte mit gemeinsamer Eigenschaft:
type Success = {
type: "success";
data: string;
};
type Error = {
type: "error";
message: string;
};
type Result = Success | Error;
function handleResult(result: Result): void {
// TypeScript weiß durch 'type', welche Properties verfügbar sind
if (result.type === "success") {
console.log("Daten:", result.data); // OK - data existiert
} else {
console.log("Fehler:", result.message); // OK - message existiert
}
}
Union mit Arrays
// Array, das Strings ODER Zahlen enthält
let mixedArray: (string | number)[] = [1, "zwei", 3, "vier"];
// Array von Strings ODER Array von Zahlen
let eitherArray: string[] | number[] = [1, 2, 3];
eitherArray = ["a", "b", "c"];
// eitherArray = [1, "zwei"]; // Fehler!
Intersection Types
Ein Intersection Type bedeutet “A UND B zusammen”:
type HasName = {
name: string;
};
type HasAge = {
age: number;
};
// Muss BEIDE Typen erfüllen
type Person = HasName & HasAge;
let person: Person = {
name: "Max",
age: 25
};
// Fehler - beide Properties sind required
let incomplete: Person = { name: "Max" }; // Fehler: 'age' fehlt
Typen erweitern
Intersection ist ideal zum Erweitern von Typen:
type BaseUser = {
id: number;
email: string;
};
type WithTimestamps = {
createdAt: Date;
updatedAt: Date;
};
type UserWithTimestamps = BaseUser & WithTimestamps;
let user: UserWithTimestamps = {
id: 1,
email: "max@example.com",
createdAt: new Date(),
updatedAt: new Date()
};
Mehrfache Intersections
type Nameable = { name: string };
type Ageable = { age: number };
type Emailable = { email: string };
type FullPerson = Nameable & Ageable & Emailable;
let person: FullPerson = {
name: "Max",
age: 25,
email: "max@example.com"
};
Union vs Intersection
type A = { a: string };
type B = { b: number };
// Union: A ODER B
type UnionAB = A | B;
let union1: UnionAB = { a: "hello" }; // OK
let union2: UnionAB = { b: 42 }; // OK
let union3: UnionAB = { a: "hi", b: 42 }; // OK (beide erfüllt)
// Intersection: A UND B
type IntersectionAB = A & B;
let inter: IntersectionAB = { a: "hello", b: 42 }; // Muss beide haben
Type Narrowing
Bei Union Types muss TypeScript wissen, mit welchem Typ du arbeitest:
typeof Guard
function process(value: string | number): void {
if (typeof value === "string") {
// Hier weiß TypeScript: value ist string
console.log(value.toUpperCase());
} else {
// Hier weiß TypeScript: value ist number
console.log(value.toFixed(2));
}
}
in Operator
type Bird = { fly: () => void };
type Fish = { swim: () => void };
function move(animal: Bird | Fish): void {
if ("fly" in animal) {
animal.fly();
} else {
animal.swim();
}
}
instanceof
function formatDate(date: Date | string): string {
if (date instanceof Date) {
return date.toISOString();
}
return new Date(date).toISOString();
}
Custom Type Guards
type Success = { type: "success"; data: string };
type Failure = { type: "failure"; error: Error };
type Result = Success | Failure;
// Custom Type Guard
function isSuccess(result: Result): result is Success {
return result.type === "success";
}
function handleResult(result: Result): void {
if (isSuccess(result)) {
console.log(result.data); // TypeScript weiß: result ist Success
} else {
console.log(result.error); // TypeScript weiß: result ist Failure
}
}
Praktische Beispiele
API Response Handler
type LoadingState = {
status: "loading";
};
type SuccessState<T> = {
status: "success";
data: T;
};
type ErrorState = {
status: "error";
error: string;
};
type AsyncState<T> = LoadingState | SuccessState<T> | ErrorState;
function renderState<T>(state: AsyncState<T>): string {
switch (state.status) {
case "loading":
return "Laden...";
case "success":
return `Daten: ${JSON.stringify(state.data)}`;
case "error":
return `Fehler: ${state.error}`;
}
}
Event Handler
type ClickEvent = {
type: "click";
x: number;
y: number;
};
type KeyEvent = {
type: "key";
key: string;
code: number;
};
type ScrollEvent = {
type: "scroll";
scrollTop: number;
};
type AppEvent = ClickEvent | KeyEvent | ScrollEvent;
function handleEvent(event: AppEvent): void {
switch (event.type) {
case "click":
console.log(`Klick bei ${event.x}, ${event.y}`);
break;
case "key":
console.log(`Taste ${event.key} gedrückt`);
break;
case "scroll":
console.log(`Gescrollt zu ${event.scrollTop}`);
break;
}
}
Mixins mit Intersection
type Printable = {
print: () => void;
};
type Serializable = {
toJSON: () => string;
};
type Loggable = {
log: (message: string) => void;
};
// Kombiniere alle Features
type FullFeature = Printable & Serializable & Loggable;
function createLogger(): FullFeature {
return {
print() { console.log("Printing..."); },
toJSON() { return "{}"; },
log(message) { console.log(message); }
};
}
Zusammenfassung
Union Types (|):
- Wert kann EINER der Typen sein
- Gut für Variablen mit mehreren möglichen Typen
- Erfordert Type Narrowing zur Verwendung
Intersection Types (&):
- Wert muss ALLE Typen erfüllen
- Gut zum Kombinieren/Erweitern von Typen
- Alle Properties müssen vorhanden sein
Type Narrowing:
typeoffür primitive Typeninfür Property-Checksinstanceoffür Klassen- Custom Type Guards für komplexe Fälle
Im nächsten Modul lernst du, wie du Funktionen in TypeScript typisierst!