Zum Inhalt springen
TypeScript Anfänger 30 min

Interfaces definieren

Lerne wie du Interfaces in TypeScript definierst und verwendest, um Objektstrukturen zu beschreiben.

Aktualisiert:

Interfaces definieren

Interfaces sind eines der Kernkonzepte in TypeScript. Sie beschreiben die Struktur von Objekten und ermöglichen es, Verträge für deinen Code zu definieren.

Interface Grundlagen

// Interface definieren
interface User {
    id: number;
    name: string;
    email: string;
}

// Objekt muss dem Interface entsprechen
const user: User = {
    id: 1,
    name: "Max",
    email: "max@example.com"
};

// Fehler bei fehlenden oder falschen Properties
const invalid: User = {
    id: 1,
    name: "Max"
    // Fehler: Property 'email' is missing
};

Optionale Properties

interface Product {
    id: number;
    name: string;
    price: number;
    description?: string;  // Optional
    discount?: number;     // Optional
}

// Beide sind gültig
const product1: Product = {
    id: 1,
    name: "Laptop",
    price: 999
};

const product2: Product = {
    id: 2,
    name: "Maus",
    price: 29,
    description: "Wireless Gaming Mouse",
    discount: 10
};

Readonly Properties

interface Config {
    readonly apiKey: string;
    readonly environment: "dev" | "prod";
    timeout: number;
}

const config: Config = {
    apiKey: "abc123",
    environment: "prod",
    timeout: 5000
};

config.timeout = 10000;     // OK
config.apiKey = "xyz";      // Fehler: Cannot assign to 'apiKey'

Methoden in Interfaces

interface Calculator {
    // Property-Syntax
    add: (a: number, b: number) => number;

    // Method-Syntax (gleichwertig)
    subtract(a: number, b: number): number;
}

const calc: Calculator = {
    add: (a, b) => a + b,
    subtract(a, b) {
        return a - b;
    }
};

console.log(calc.add(5, 3));       // 8
console.log(calc.subtract(5, 3));  // 2

Index Signatures

Für Objekte mit dynamischen Keys:

interface StringMap {
    [key: string]: string;
}

const translations: StringMap = {
    hello: "Hallo",
    goodbye: "Auf Wiedersehen",
    thanks: "Danke"
};

translations.welcome = "Willkommen";  // OK

// Mit zusätzlichen festen Properties
interface UserScores {
    name: string;                    // Fest
    [subject: string]: string | number;  // Dynamisch (muss kompatibel sein)
}

const scores: UserScores = {
    name: "Max",
    math: 95,
    english: 88,
    science: 92
};

Callable Interfaces

Interfaces für Funktionen:

interface Formatter {
    (value: number): string;
}

const currencyFormatter: Formatter = (value) => {
    return `${value.toFixed(2)} €`;
};

console.log(currencyFormatter(19.99));  // "19.99 €"

// Mit Properties
interface FormatterWithOptions {
    (value: number): string;
    precision: number;
    currency: string;
}

const formatter: FormatterWithOptions = Object.assign(
    (value: number) => `${value.toFixed(formatter.precision)} ${formatter.currency}`,
    { precision: 2, currency: "€" }
);

Interfaces für Klassen

interface Printable {
    print(): void;
}

interface Serializable {
    toJSON(): string;
}

class Document implements Printable, Serializable {
    constructor(private content: string) {}

    print(): void {
        console.log(this.content);
    }

    toJSON(): string {
        return JSON.stringify({ content: this.content });
    }
}

const doc = new Document("Hello World");
doc.print();
console.log(doc.toJSON());

Extending Interfaces

Interfaces können andere Interfaces erweitern:

interface Animal {
    name: string;
    age: number;
}

interface Pet extends Animal {
    owner: string;
    vaccinated: boolean;
}

interface Dog extends Pet {
    breed: string;
    bark(): void;
}

const myDog: Dog = {
    name: "Buddy",
    age: 3,
    owner: "Max",
    vaccinated: true,
    breed: "Golden Retriever",
    bark() {
        console.log("Woof!");
    }
};

Mehrfache Vererbung

interface Timestamped {
    createdAt: Date;
    updatedAt: Date;
}

interface Identifiable {
    id: number;
}

interface Nameable {
    name: string;
}

// Kombiniere alle drei
interface Entity extends Timestamped, Identifiable, Nameable {
    // Zusätzliche Properties
    active: boolean;
}

const entity: Entity = {
    id: 1,
    name: "Test Entity",
    createdAt: new Date(),
    updatedAt: new Date(),
    active: true
};

Declaration Merging

Interfaces mit gleichem Namen werden zusammengeführt:

interface User {
    name: string;
}

interface User {
    email: string;
}

// User hat jetzt beide Properties
const user: User = {
    name: "Max",
    email: "max@example.com"
};

Dies ist nützlich für das Erweitern von Bibliotheks-Interfaces:

// Erweitere Window-Interface
declare global {
    interface Window {
        myCustomProperty: string;
    }
}

window.myCustomProperty = "Hello";

Generische Interfaces

interface Repository<T> {
    getAll(): T[];
    getById(id: number): T | undefined;
    create(item: T): T;
    update(id: number, item: Partial<T>): T | undefined;
    delete(id: number): boolean;
}

interface User {
    id: number;
    name: string;
    email: string;
}

class UserRepository implements Repository<User> {
    private users: User[] = [];

    getAll(): User[] {
        return this.users;
    }

    getById(id: number): User | undefined {
        return this.users.find(u => u.id === id);
    }

    create(item: User): User {
        this.users.push(item);
        return item;
    }

    update(id: number, item: Partial<User>): User | undefined {
        const user = this.getById(id);
        if (user) {
            Object.assign(user, item);
        }
        return user;
    }

    delete(id: number): boolean {
        const index = this.users.findIndex(u => u.id === id);
        if (index !== -1) {
            this.users.splice(index, 1);
            return true;
        }
        return false;
    }
}

Praktische Beispiele

API Response Interface

interface ApiResponse<T> {
    success: boolean;
    data?: T;
    error?: {
        code: string;
        message: string;
    };
    meta?: {
        page: number;
        totalPages: number;
        totalItems: number;
    };
}

interface User {
    id: number;
    name: string;
}

function handleResponse(response: ApiResponse<User[]>): void {
    if (response.success && response.data) {
        response.data.forEach(user => {
            console.log(user.name);
        });
    } else if (response.error) {
        console.error(`Error ${response.error.code}: ${response.error.message}`);
    }
}

Event System

interface Event {
    type: string;
    timestamp: Date;
}

interface ClickEvent extends Event {
    type: "click";
    x: number;
    y: number;
    target: HTMLElement;
}

interface KeyboardEvent extends Event {
    type: "keyboard";
    key: string;
    keyCode: number;
    modifiers: {
        ctrl: boolean;
        alt: boolean;
        shift: boolean;
    };
}

interface EventHandler<T extends Event> {
    (event: T): void;
}

interface EventEmitter {
    on<T extends Event>(type: T["type"], handler: EventHandler<T>): void;
    emit<T extends Event>(event: T): void;
}

Form Configuration

interface FormField {
    name: string;
    label: string;
    type: "text" | "email" | "password" | "number" | "select";
    required?: boolean;
    placeholder?: string;
    validation?: {
        minLength?: number;
        maxLength?: number;
        pattern?: RegExp;
        custom?: (value: string) => boolean;
    };
}

interface SelectField extends FormField {
    type: "select";
    options: { value: string; label: string }[];
}

interface FormConfig {
    id: string;
    title: string;
    fields: (FormField | SelectField)[];
    onSubmit: (data: Record<string, string>) => void;
}

const loginForm: FormConfig = {
    id: "login",
    title: "Anmelden",
    fields: [
        {
            name: "email",
            label: "E-Mail",
            type: "email",
            required: true,
            validation: { pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ }
        },
        {
            name: "password",
            label: "Passwort",
            type: "password",
            required: true,
            validation: { minLength: 8 }
        }
    ],
    onSubmit: (data) => console.log("Login:", data)
};

Zusammenfassung

  • Interfaces beschreiben Objektstrukturen
  • Optionale Properties mit ?
  • Readonly Properties mit readonly
  • Methoden mit Property- oder Method-Syntax
  • Index Signatures für dynamische Keys
  • Extending für Vererbung
  • Declaration Merging für Erweiterungen

Best Practices:

  • Nutze Interfaces für Objekt-Shapes
  • Bevorzuge readonly wo möglich
  • Erweitere bestehende Interfaces statt Duplikation
  • Halte Interfaces fokussiert und klein

Im nächsten Kapitel lernst du Type Aliases!

Zurück zum TypeScript Kurs