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

Generics-Grundlagen in TypeScript

Generics-Grundlagen

~17 Min. Lesezeit Zuletzt aktualisiert am 8. August 2026

Unsere Bibliothek-Klasse kann bisher NUR Buch-Objekte verwalten – was, wenn wir dasselbe Muster (Bestand verwalten, suchen, ausleihen) auch für Hörbücher, DVDs oder Zeitschriften wiederverwenden wollen, OHNE die Klasse zu duplizieren? Generics sind die Antwort: "Typ-Parameter", die eine Klasse/Funktion für BELIEBIGE Typen wiederverwendbar machen.

Das Problem ohne Generics

function ersteElement(liste: number[]): number {
  return liste[0];
}

function ersteElementString(liste: string[]): string {
  return liste[0];
}
// Für JEDEN Typ eine eigene, fast identische Funktion? Das skaliert nicht.

Die generische Lösung: ein Typ-Parameter

function erstesElement<T>(liste: T[]): T {
  return liste[0];
}

erstesElement<number>([1, 2, 3]);        // T wird zu number
erstesElement<string>(['a', 'b']);       // T wird zu string
erstesElement([true, false]);              // T wird AUTOMATISCH zu boolean abgeleitet -
                                            // die spitzen Klammern sind meist WEGLASSBAR

<T> ist ein PLATZHALTER für "irgendein Typ, der beim tatsächlichen Aufruf feststeht" – T ist reine Konvention (steht für "Type"), jeder gültige Bezeichner funktioniert. Der ENTSCHEIDENDE Vorteil gegenüber any: TypeScript weiß bei erstesElement([1, 2, 3]), dass das Ergebnis number ist, NICHT any – volle Typsicherheit bleibt erhalten, nur generisch statt fest verdrahtet.

Praxis: ein generisches Repository

Wir bauen eine generische Repository<T>-Klasse, die das GEMEINSAME Muster (Liste verwalten, suchen, hinzufügen) für JEDEN Medientyp bereitstellt:

src/repository/Repository.ts
export class Repository<T> {
  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);
  }
}
import { Repository } from './repository/Repository.js';
import { Buch } from './models/Buch.js';
import { Hoerbuch } from './models/Hoerbuch.js';

const buchRepository = new Repository<Buch>();
buchRepository.hinzufuegen(new Buch(/* ... */));

const hoerbuchRepository = new Repository<Hoerbuch>();
hoerbuchRepository.hinzufuegen(new Hoerbuch(/* ... */));

// EINE Klasse, ZWEI vollständig typsichere, unabhängige Verwendungen -
// buchRepository.finde(...) liefert Buch | undefined, NICHT Hoerbuch | undefined

Repository<Buch> und Repository<Hoerbuch> sind zwei UNTERSCHIEDLICHE, spezialisierte Versionen DERSELBEN generischen Klasse – TypeScript hält sie AUSEINANDER: buchRepository.finde(...) gibt garantiert einen Buch | undefined zurück, nie einen Hoerbuch.

Generics bei Funktionen vs. Klassen

// Generische Funktion - T wird PRO AUFRUF neu bestimmt:
function letzeElement<T>(liste: T[]): T | undefined {
  return liste[liste.length - 1];
}

// Generische Klasse - T wird EINMAL bei new festgelegt, gilt für die GESAMTE Instanz:
const repo = new Repository<Buch>(); // T = Buch für IMMER, für DIESE repo-Instanz

Achtung: Häufige Verwechslung: bei generischen FUNKTIONEN kann T bei JEDEM Aufruf unterschiedlich sein, bei generischen KLASSEN wird T EINMAL bei der Instanziierung (new Repository<Buch>()) festgelegt und gilt UNVERÄNDERLICH für diese eine Instanz – ein zweiter Aufruf von hinzufuegen() kann NICHT plötzlich einen anderen Typ akzeptieren.

Tipp: Faustregel: sobald Sie Code SCHREIBEN, der für any funktionieren würde, aber Sie WOLLEN eigentlich Typsicherheit behalten ("das Ergebnis soll denselben Typ haben wie die Eingabe"), ist das ein starker Hinweis auf Generics. any verwirft Typinformation KOMPLETT, Generics bewahren sie – nur eben FLEXIBEL statt fest verdrahtet.