Skip to content

Kapitel 4: Interfaces

🎯 Lernziele

In diesem Kapitel lernen Sie:

  • ✅ Was Interfaces sind und warum sie wichtig sind
  • ✅ Objekt-Interfaces definieren
  • ✅ Optionale und readonly Eigenschaften
  • ✅ Index Signatures (beliebige Eigenschaften)
  • ✅ Interface Vererbung
  • ✅ Unterschied zwischen interface und type

4.1 Was ist ein Interface?

📖 Einfache Definition

Ein Interface definiert die Struktur eines Objekts (welche Eigenschaften und Methoden es haben sollte).

💡 Kernkonzept

Interface = Vertrag (Contract)

Ein Interface sagt: "Jedes Objekt, das dieses Interface implementiert, muss diese Eigenschaften und Methoden haben."

🎨 Visuelle Erklärung

┌─────────────────────────────┐
│  Interface Person           │
├─────────────────────────────┤
│  + name: string            │
│  + age: number            │
│  + greet(): void          │
└─────────────────────────────┘

┌─────────────────────────────┐
│  Objekt user                │
├─────────────────────────────┤
│  name = "Max"              │
│  age = 25                  │
│  greet() { ... }           │
└─────────────────────────────┘

4.2 Objekt-Interface definieren

💻 Code-Beispiel

typescript
// Interface definieren
interface Person {
  name: string;
  age: number;
  email: string;
}

// Interface verwenden
const user: Person = {
  name: "Max",
  age: 25,
  email: "max@example.com"
};

❌ Häufige Fehler

typescript
interface Person {
  name: string;
  age: number;
}

// ❌ Fehler: Eigenschaft fehlt
const user1: Person = {
  name: "Max"
};

// ❌ Fehler: Eigenschaft ist vom falschen Typ
const user2: Person = {
  name: "Max",
  age: "25"  // string statt number!
};

// ❌ Fehler: Zusätzliche Eigenschaft (im Strict Mode)
const user3: Person = {
  name: "Max",
  age: 25,
  email: "max@example.com"  // Nicht in Interface definiert!
};

✅ Zusätzliche Eigenschaften erlauben

typescript
// Methode 1: Index Signature (später behandelt)
interface Person {
  name: string;
  age: number;
  [key: string]: any;  // Erlaubt zusätzliche Eigenschaften
}

// Methode 2: Type Assertion verwenden
interface Person {
  name: string;
  age: number;
}

const user = {
  name: "Max",
  age: 25,
  email: "max@example.com"
} as Person;  // Nicht empfohlen!

4.3 Optionale Eigenschaften (?)

💻 Code-Beispiel

typescript
interface User {
  id: number;
  name: string;
  email?: string;  // Optional
  age?: number;    // Optional
}

// Erlaubt
const user1: User = {
  id: 1,
  name: "Max"
};

// Auch erlaubt
const user2: User = {
  id: 2,
  name: "Maria",
  email: "maria@example.com",
  age: 30
};

⚠️ Zugriff auf optionale Eigenschaften

typescript
interface User {
  name: string;
  age?: number;
}

function printAge(user: User): void {
  // ❌ Fehler: age könnte undefined sein
  console.log(user.age.toFixed(2));

  // ✅ Richtig: Prüfen
  if (user.age !== undefined) {
    console.log(user.age.toFixed(2));
  }

  // ✅ Richtig: Optional Chaining
  console.log(user.age?.toFixed(2));
}

4.4 Readonly Eigenschaften (readonly)

💻 Code-Beispiel

typescript
interface User {
  readonly id: number;  // Kann nur einmal zugewiesen werden
  name: string;
  age?: number;
}

const user: User = {
  id: 1,
  name: "Max"
};

user.name = "Maria";  // ✅ Erlaubt
user.age = 30;        // ✅ Erlaubt
user.id = 2;          // ❌ Fehler: Cannot assign to 'id' because it is a read-only property

🎨 Readonly Array

typescript
// Readonly Array
const numbers: readonly number[] = [1, 2, 3];
numbers.push(4);  // ❌ Fehler
numbers[0] = 10; // ❌ Fehler

// Alternative: ReadonlyArray<T>
const numbers2: ReadonlyArray<number> = [1, 2, 3];

4.5 Index Signatures (Beliebige Eigenschaften)

💻 Code-Beispiel

typescript
// Index Signature: Erlaubt beliebige Eigenschaften
interface StringDictionary {
  [key: string]: string;
}

const dict: StringDictionary = {
  "de": "Hallo",
  "en": "Hello",
  "es": "Hola"
};

// Auch dynamische Keys
dict["fr"] = "Bonjour";

🎨 Praxis-Beispiel

typescript
// API-Konfiguration
interface Config {
  apiUrl: string;
  timeout: number;
  [key: string]: any;  // Erlaubt zusätzliche Konfigurationen
}

const config: Config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3,        // Zusätzlich
  logLevel: "debug"  // Zusätzlich
};

4.6 Interface Vererbung (extends)

💻 Code-Beispiel

typescript
// Basis-Interface
interface Person {
  name: string;
  age: number;
}

// Abgeleitetes Interface
interface Employee extends Person {
  employeeId: number;
  department: string;
}

// Verwendung
const emp: Employee = {
  name: "Max",
  age: 25,
  employeeId: 12345,
  department: "IT"
};

🎨 Mehrfach-Vererbung

typescript
interface CanFly {
  fly(): void;
}

interface CanSwim {
  swim(): void;
}

// Interface mit mehreren Eltern-Interfaces
interface Duck extends CanFly, CanSwim {
  name: string;
}

const donald: Duck = {
  name: "Donald",
  fly() { console.log("Fliegt..."); },
  swim() { console.log("Schwimmt..."); }
};

4.7 Interface vs Type Alias

📊 Vergleich

Featureinterfacetype
Objekt-Typen
Vererbungextends& (Intersection)
Union Types
Primitive Typen
Declaration Merging✅ (automatisch)

💻 Code-Beispiel

typescript
// Interface: Vererbung
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

// Type Alias: Intersection
type Animal = {
  name: string;
};

type Dog = Animal & {
  breed: string;
};

// Type Alias: Union Type (Interface kann das nicht)
type Status = "pending" | "success" | "error";

// Type Alias: Primitive Typen (Interface kann das nicht)
type ID = string | number;

💡 Empfehlung

  • Verwenden Sie interface für: Objekt-Typen, die erweitert werden können
  • Verwenden Sie type für: Union Types, Primitive Typen, Tuples, komplexe Typ-Kombinationen

4.8 Interface für Funktionen und Arrays

💻 Interface für Funktionen

typescript
// Interface für Funktionen
interface GreetFunction {
  (name: string): string;
}

const greet: GreetFunction = (name) => `Hallo, ${name}!`;

console.log(greet("Max"));  // Ausgabe: Hallo, Max!

💻 Interface für Arrays

typescript
// Interface für Arrays
interface StringArray {
  [index: number]: string;
}

const arr: StringArray = ["a", "b", "c"];
console.log(arr[0]);  // Ausgabe: a

4.9 Praxis: Datenstrukturen mit Interfaces definieren

💻 Vollständiges Beispiel

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

// User Interface
interface User {
  id: number;
  name: string;
  email: string;
  age?: number;
  readonly createdAt: Date;
}

// API Response mit User-Daten
const response: ApiResponse<User> = {
  success: true,
  data: {
    id: 1,
    name: "Max",
    email: "max@example.com",
    createdAt: new Date()
  },
  message: "Benutzer erfolgreich abgerufen"
};

console.log(response.data.name);  // Ausgabe: Max

📝 Zusammenfassung

In diesem Kapitel haben Sie gelernt:

  • Interface definiert die Struktur eines Objekts
  • Optionale Eigenschaften (?): Müssen nicht vorhanden sein
  • Readonly Eigenschaften (readonly): Können nicht geändert werden
  • Index Signatures ([key: string]: any): Erlauben beliebige Eigenschaften
  • Interface Vererbung (extends): Erweitert bestehende Interfaces
  • Interface vs Type: Wann welches verwenden?

🎯 Nächstes Kapitel

Im Kapitel 5 lernen wir Funktionstypen (Parameter, Rückgabewerte, Overloads, etc.).

👉 Weiter zu Kapitel 5: Funktionstypen


❓ Häufig gestellte Fragen

F: Soll ich interface oder type verwenden?

A: Für Objekt-Typen: interface (bessere Erweiterbarkeit). Für Union Types, Primitive Typen: type.

F: Kann ich ein Interface nachträglich ändern?

A: Ja! TypeScript unterstützt "Declaration Merging" bei Interfaces:

typescript
interface User {
  name: string;
}

interface User {  // Wird zusammengeführt!
  age: number;
}

// User hat jetzt name UND age

F: Warum "readonly" verwenden?

A: Um unbeabsichtigte Änderungen zu verhindern (immutability).


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

Frei für alle Anfänger