Generics mit Constraints in TypeScript
Generics mit Constraints
~15 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026
Unser Repository<T> aus Kapitel 17 funktioniert für JEDEN Typ – aber manche Operationen (z. B. "finde per ISBN") ergeben nur Sinn, wenn T GARANTIERT eine isbn-Eigenschaft hat. Constraints (Einschränkungen) schränken ein, WELCHE Typen als T überhaupt erlaubt sind.
Das Problem ohne Constraint
export class Repository<T> {
protected elemente: T[] = [];
findePerIsbn(isbn: string): T | undefined {
return this.elemente.find((el) => el.isbn === isbn); // Fehler!
// TypeScript weiß NICHT, dass T überhaupt eine 'isbn'-Eigenschaft hat -
// T könnte theoretisch 'string' oder 'number' sein, OHNE jegliche Eigenschaften
}
}Die Lösung: extends als Constraint
export interface HatIsbn {
isbn: string;
}import { HatIsbn } from '../models/HatIsbn.js';
export class Repository<T extends HatIsbn> {
protected elemente: T[] = [];
findePerIsbn(isbn: string): T | undefined {
return this.elemente.find((el) => el.isbn === isbn); // ✓ jetzt erlaubt!
}
}
new Repository<Buch>(); // ✓ Buch HAT eine isbn-Eigenschaft
new Repository<string>(); // ✗ Fehler - string erfüllt HatIsbn NICHTT extends HatIsbn bedeutet: "T darf JEDER Typ sein, SOLANGE er MINDESTENS die Form von HatIsbn erfüllt" – T kann trotzdem WEITERE Eigenschaften haben (unser Buch hat viel mehr als nur isbn), muss aber MINDESTENS isbn: string besitzen.
Mehrere Anforderungen kombinieren
interface HatStatus {
status: string;
}
function zeigeVerfuegbareElemente<T extends HatIsbn & HatStatus>(elemente: T[]): T[] {
return elemente.filter((el) => el.status === 'verfuegbar');
}T extends HatIsbn & HatStatus kombiniert Intersection Types (Kapitel 9) mit Generics-Constraints: T muss BEIDE Interfaces gleichzeitig erfüllen.
Bonus: keyof für typsicheren Eigenschaftszugriff
function holeEigenschaft<T, K extends keyof T>(objekt: T, schluessel: K): T[K] {
return objekt[schluessel];
}
const buch = { titel: 'Test', seitenzahl: 300 };
holeEigenschaft(buch, 'titel'); // ✓ Rückgabetyp: string
holeEigenschaft(buch, 'seitenzahl'); // ✓ Rückgabetyp: number
holeEigenschaft(buch, 'autor'); // ✗ Fehler - 'autor' existiert nicht auf buchK extends keyof T ist ein FORTGESCHRITTENES, aber extrem nützliches Muster: keyof T ist eine Union aus ALLEN Eigenschaftsnamen von T als String-Literal-Types (hier also 'titel' | 'seitenzahl') – K MUSS einer davon sein, und der Rückgabetyp T[K] passt sich AUTOMATISCH an den jeweils übergebenen Schlüssel an.
Praxis: Repository konsequent mit Constraint nutzen
import { HatIsbn } from '../models/HatIsbn.js';
export class Repository<T extends HatIsbn> {
protected elemente: T[];
constructor(startElemente: T[] = []) {
this.elemente = startElemente;
}
alle(): readonly T[] {
return this.elemente;
}
hinzufuegen(element: T): void {
this.elemente.push(element);
}
anzahl(): number {
return this.elemente.length;
}
finde(praedikat: (element: T) => boolean): T | undefined {
return this.elemente.find(praedikat);
}
findePerIsbn(isbn: string): T | undefined {
return this.elemente.find((el) => el.isbn === isbn);
}
}Buch und Hoerbuch (Kapitel 13) erfüllen BEIDE HatIsbn automatisch, da Medium die isbn-Eigenschaft bereits mitbringt – KEINE Änderung an den bestehenden Modell-Klassen nötig.
Tipp: Faustregel: fügen Sie einen Constraint (extends) hinzu, SOBALD Sie innerhalb einer generischen Funktion/Klasse auf eine SPEZIFISCHE Eigenschaft von T zugreifen müssen. Ohne Zugriff auf spezifische Eigenschaften (wie unser erstesElement<T> aus Kapitel 17) ist GAR KEIN Constraint nötig – je WENIGER Einschränkungen, desto WIEDERVERWENDBARER die Funktion.