Skip to content

Kapitel 7: Generics (Generische Typen)

🎯 Lernziele

In diesem Kapitel lernen Sie:

  • ✅ Was Generics sind und warum sie wichtig sind
  • ✅ Generische Funktionen
  • ✅ Generische Interfaces
  • ✅ Generische Klassen
  • ✅ Generische Constraints (Einschränkungen)
  • ✅ Standard-Generik-Typen
  • ✅ Praxis: Generische Tool-Funktionen

7.1 Was sind Generics?

📖 Einfache Definition

Generics erlauben Ihnen, Typparameter zu definieren, die zur Laufzeit mit einem konkreten Typ ersetzt werden.

💡 Kernkonzept

Generics = Platzhalter für Typen

Wie Funktionsparameter Werte repräsentieren, repräsentieren Generics Typen.

🎨 Visuelle Erklärung

Ohne Generics:
┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
│  function       │     │  function       │     │  function       │
│  identity(x:T)  │     │  identity(x:string)  │  identity(x:number)  │
│  : T            │     │  : string       │     │  : number       │
└─────────────────┘     └─────────────────┘     └─────────────────┘
      ↓                       ↓                       ↓
  Duplizierter Code      Duplizierter Code      Schwer zu warten

Mit Generics:
┌─────────────────┐
│  function       │
│  identity<T>(x:T) │  ← T ist ein Typparameter
│  : T            │
└─────────────────┘

  Ein einziger, wiederverwendbarer Code!

7.2 Warum Generics verwenden?

❌ Problem ohne Generics

typescript
// ❌ Lösung 1: any verwenden (verliert Typsicherheit)
function identity1(arg: any): any {
  return arg;
}

let result1 = identity1("Hallo");
// result1 ist vom Typ any (keine Autovervollständigung!)

// ❌ Lösung 2: Überladung (viel Code)
function identity2(x: string): string;
function identity2(x: number): number;
function identity2(x: any): any {
  return x;
}

✅ Lösung mit Generics

typescript
// ✅ Generische Funktion
function identity<T>(arg: T): T {
  return arg;
}

// Typ wird automatisch abgeleitet
let result1 = identity("Hallo");  // Typ: string
let result2 = identity(123);       // Typ: number

// Oder Typ explizit angeben
let result3 = identity<string>("Welt");

7.3 Generische Funktionen

📖 Grundlegende Syntax

typescript
function funktionsname<T>(parameter: T): T {
  // ...
}

💻 Code-Beispiel

typescript
// Einfache generische Funktion
function identity<T>(arg: T): T {
  return arg;
}

// Verwendung
const str = identity<string>("Hallo");
const num = identity<number>(123);
const bool = identity<boolean>(true);

console.log(str);   // Ausgabe: Hallo
console.log(num);   // Ausgabe: 123
console.log(bool);  // Ausgabe: true

🎨 Type Inference (Typableitung)

TypeScript kann den Typ oft automatisch erkennen:

typescript
// Typ wird automatisch abgeleitet (keine Angabe erforderlich)
const str = identity("Hallo");  // T wird zu string abgeleitet
const num = identity(123);      // T wird zu number abgeleitet

7.4 Mehrere Typparameter

💻 Code-Beispiel

typescript
// Mehrere Typparameter
function pair<K, V>(key: K, value: V): [K, V] {
  return [key, value];
}

// Verwendung
const p1 = pair(1, "Eins");        // [number, string]
const p2 = pair("name", "Max");    // [string, string]
const p3 = pair(true, 123);        // [boolean, number]

console.log(p1);  // Ausgabe: [1, "Eins"]
console.log(p2);  // Ausgabe: ["name", "Max"]
console.log(p3);  // Ausgabe: [true, 123]

7.5 Generische Interfaces

📖 Einfache Definition

Interfaces können generische Typparameter haben.

💻 Code-Beispiel

typescript
// Generisches Interface
interface Response<T> {
  success: boolean;
  data: T;
  message?: string;
}

// Verwendung mit verschiedenen Typen
interface User {
  id: number;
  name: string;
}

interface Product {
  id: number;
  title: string;
  price: number;
}

// Response mit User-Daten
const userResponse: Response<User> = {
  success: true,
  data: { id: 1, name: "Max" }
};

// Response mit Product-Daten
const productResponse: Response<Product> = {
  success: true,
  data: { id: 101, title: "Laptop", price: 999.99 }
};

console.log(userResponse.data.name);    // Ausgabe: Max
console.log(productResponse.data.price); // Ausgabe: 999.99

7.6 Generische Klassen

📖 Einfache Definition

Klassen können generische Typparameter haben.

💻 Code-Beispiel

typescript
// Generische Klasse
class Storage<T> {
  private data: T;

  constructor(data: T) {
    this.data = data;
  }

  getData(): T {
    return this.data;
  }

  setData(data: T): void {
    this.data = data;
  }
}

// Verwendung
const stringStorage = new Storage<string>("Hallo");
console.log(stringStorage.getData());  // Ausgabe: Hallo

stringStorage.setData("Welt");
console.log(stringStorage.getData());  // Ausgabe: Welt

// number
const numberStorage = new Storage<number>(123);
console.log(numberStorage.getData());  // Ausgabe: 123

7.7 Generische Constraints (Einschränkungen)

📖 Einfache Definition

Constraints schränken ein, welche Typen für einen Typparameter erlaubt sind.

💻 Code-Beispiel

typescript
// Constraint: T muss ein Objekt mit der Eigenschaft length haben
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): void {
  console.log(arg.length);
}

// ✅ Erlaubt
logLength("Hallo");        // string hat length
logLength([1, 2, 3]);    // Array hat length

// ❌ Fehler
logLength(123);            // number hat keine length

🎨 Weiteres Beispiel

typescript
// Constraint: T muss ein Objekt sein
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = {
  name: "Max",
  age: 25
};

// ✅ Erlaubt
const name = getProperty(user, "name");  // Typ: string
const age = getProperty(user, "age");    // Typ: number

// ❌ Fehler: "email" existiert nicht in user
const email = getProperty(user, "email");

7.8 Standard-Generik-Typen (Default Type Parameters)

📖 Einfache Definition

Sie können Standardtypen für Generics angeben.

💻 Code-Beispiel

typescript
// Standardtyp: string
interface Container<T = string> {
  value: T;
}

// ✅ Mit Typangabe
const numContainer: Container<number> = { value: 123 };

// ✅ Ohne Typangabe (verwendet Standard: string)
const strContainer: Container = { value: "Hallo" };

7.9 Praxis: Generische Tool-Funktionen

💻 Vollständiges Beispiel

typescript
// 1. Rückgabewert-Typ abrufen
function getReturnType<T>(fn: (...args: any[]) => T): T {
  // ...
  return {} as T;
}

// 2. Array umdrehen
function reverseArray<T>(array: T[]): T[] {
  return array.reverse();
}

// 3. Erstes Element abrufen
function getFirstElement<T>(array: T[]): T | undefined {
  return array[0];
}

// 4. Eindeutigen Wert erstellen
function unique<T>(array: T[]): T[] {
  return [...new Set(array)];
}

// Verwendung
console.log(reverseArray([1, 2, 3]));  // Ausgabe: [3, 2, 1]
console.log(getFirstElement(["a", "b", "c"]));  // Ausgabe: a
console.log(unique([1, 2, 2, 3, 3]));  // Ausgabe: [1, 2, 3]

📝 Zusammenfassung

In diesem Kapitel haben Sie gelernt:

  • Generics ermöglichen typsichere, wiederverwendbare Funktionen und Klassen
  • Generische Funktionen: function identity<T>(arg: T): T
  • Generische Interfaces: interface Response<T>
  • Generische Klassen: class Storage<T>
  • Constraints: T extends ... schränkt erlaubte Typen ein
  • Standardtypen: interface Container<T = string>

🎯 Nächstes Kapitel

Im Kapitel 8 lernen wir tsconfig.json (TypeScript-Konfiguration für Projekte)!

👉 Weiter zu Kapitel 8: tsconfig.json


❓ Häufig gestellte Fragen

F: Wann sollte ich Generics verwenden?

A: Wenn Sie Funktionen oder Klassen schreiben, die mit verschiedenen Typen arbeiten sollen, aber dabei typsicher bleiben wollen.

F: Was ist der Unterschied zwischen any und Generics?

A:

  • any: Deaktiviert Typprüfung (unsicher)
  • any: Verliert Typinformationen
  • Generics: Behält Typinformationen bei (sicher)

F: Kann ich mehrere Constraints haben?

A: Ja! T extends A & B (Intersection Type)


🎉 Herzlichen Glückwunsch! Sie haben Kapitel 7 abgeschlossen!

Frei für alle Anfänger