Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

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

src/models/HatIsbn.ts
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 NICHT

T 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 buch

K 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

src/repository/Repository.ts
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.