Zum Inhalt springen
TypeScript Anfänger 25 min

Klassen Grundlagen

Lerne die Grundlagen von Klassen in TypeScript - Properties, Konstruktoren und Methoden.

Aktualisiert:

Klassen Grundlagen

TypeScript erweitert JavaScript-Klassen um Typisierung und zusätzliche Features. In diesem Kapitel lernst du die Grundlagen von Klassen in TypeScript.

Klassen definieren

class User {
    // Properties mit Typen
    name: string;
    age: number;
    email: string;

    // Konstruktor
    constructor(name: string, age: number, email: string) {
        this.name = name;
        this.age = age;
        this.email = email;
    }

    // Methode
    greet(): string {
        return `Hallo, ich bin ${this.name}!`;
    }
}

const user = new User("Max", 25, "max@example.com");
console.log(user.greet());  // "Hallo, ich bin Max!"

Parameter Properties

Kurzschreibweise für Constructor-Properties:

class User {
    // Properties werden automatisch erstellt und zugewiesen
    constructor(
        public name: string,
        public age: number,
        public email: string
    ) {}

    greet(): string {
        return `Hallo, ich bin ${this.name}!`;
    }
}

// Gleiches Ergebnis wie vorher
const user = new User("Max", 25, "max@example.com");

Readonly Properties

Properties, die nur einmal gesetzt werden können:

class Config {
    constructor(
        public readonly apiUrl: string,
        public readonly timeout: number
    ) {}
}

const config = new Config("https://api.example.com", 5000);
// config.apiUrl = "other";  // Fehler: Cannot assign to 'apiUrl'

Optionale Properties

class User {
    constructor(
        public name: string,
        public email: string,
        public age?: number,           // Optional
        public bio: string = "No bio"  // Mit Default
    ) {}
}

const user1 = new User("Max", "max@example.com");
const user2 = new User("Anna", "anna@example.com", 30, "Developer");

Methoden

class Calculator {
    private result: number = 0;

    add(n: number): this {
        this.result += n;
        return this;
    }

    subtract(n: number): this {
        this.result -= n;
        return this;
    }

    multiply(n: number): this {
        this.result *= n;
        return this;
    }

    getResult(): number {
        return this.result;
    }

    reset(): this {
        this.result = 0;
        return this;
    }
}

// Method Chaining
const result = new Calculator()
    .add(10)
    .multiply(2)
    .subtract(5)
    .getResult();

console.log(result);  // 15

Getter und Setter

class Circle {
    constructor(private _radius: number) {}

    // Getter
    get radius(): number {
        return this._radius;
    }

    // Setter mit Validierung
    set radius(value: number) {
        if (value <= 0) {
            throw new Error("Radius muss positiv sein");
        }
        this._radius = value;
    }

    // Computed Property
    get area(): number {
        return Math.PI * this._radius ** 2;
    }

    get circumference(): number {
        return 2 * Math.PI * this._radius;
    }
}

const circle = new Circle(5);
console.log(circle.area);          // 78.54...
console.log(circle.circumference); // 31.42...

circle.radius = 10;
console.log(circle.area);          // 314.16...

Statische Members

Properties und Methoden auf der Klasse selbst:

class MathUtils {
    static readonly PI = 3.14159;

    static square(n: number): number {
        return n * n;
    }

    static cube(n: number): number {
        return n * n * n;
    }

    static factorial(n: number): number {
        if (n <= 1) return 1;
        return n * MathUtils.factorial(n - 1);
    }
}

// Zugriff ohne Instanz
console.log(MathUtils.PI);           // 3.14159
console.log(MathUtils.square(5));    // 25
console.log(MathUtils.factorial(5)); // 120

Statische Factory Methods

class User {
    private constructor(
        public id: number,
        public name: string,
        public email: string
    ) {}

    // Factory Methods
    static create(name: string, email: string): User {
        const id = Math.floor(Math.random() * 10000);
        return new User(id, name, email);
    }

    static fromJSON(json: string): User {
        const data = JSON.parse(json);
        return new User(data.id, data.name, data.email);
    }
}

const user1 = User.create("Max", "max@example.com");
const user2 = User.fromJSON('{"id": 1, "name": "Anna", "email": "anna@example.com"}');

Index Signatures in Klassen

class Dictionary {
    [key: string]: string | ((key: string) => string | undefined);

    get(key: string): string | undefined {
        return this[key] as string | undefined;
    }

    set(key: string, value: string): void {
        this[key] = value;
    }
}

const dict = new Dictionary();
dict.set("hello", "Hallo");
dict.set("goodbye", "Auf Wiedersehen");

console.log(dict.get("hello"));    // "Hallo"
console.log(dict["goodbye"]);      // "Auf Wiedersehen"

Klassen und Interfaces

interface Printable {
    print(): void;
}

interface Serializable {
    toJSON(): string;
}

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

    print(): void {
        console.log(`=== ${this.title} ===`);
        console.log(this.content);
    }

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

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

Praktische Beispiele

Event Emitter

type EventHandler<T> = (data: T) => void;

class EventEmitter<Events extends Record<string, unknown>> {
    private handlers = new Map<keyof Events, Function[]>();

    on<K extends keyof Events>(
        event: K,
        handler: EventHandler<Events[K]>
    ): void {
        const existing = this.handlers.get(event) || [];
        this.handlers.set(event, [...existing, handler]);
    }

    off<K extends keyof Events>(
        event: K,
        handler: EventHandler<Events[K]>
    ): void {
        const existing = this.handlers.get(event) || [];
        this.handlers.set(event, existing.filter(h => h !== handler));
    }

    emit<K extends keyof Events>(event: K, data: Events[K]): void {
        const handlers = this.handlers.get(event) || [];
        handlers.forEach(handler => handler(data));
    }
}

// Verwendung
type AppEvents = {
    login: { userId: number };
    logout: { userId: number };
    error: { message: string };
};

const events = new EventEmitter<AppEvents>();

events.on("login", (data) => {
    console.log(`User ${data.userId} logged in`);
});

events.emit("login", { userId: 1 });

Repository Pattern

class Repository<T extends { id: number }> {
    private items: T[] = [];

    findAll(): T[] {
        return [...this.items];
    }

    findById(id: number): T | undefined {
        return this.items.find(item => item.id === id);
    }

    create(item: T): T {
        this.items.push(item);
        return item;
    }

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

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

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

const userRepo = new Repository<User>();
userRepo.create({ id: 1, name: "Max", email: "max@example.com" });
userRepo.create({ id: 2, name: "Anna", email: "anna@example.com" });

console.log(userRepo.findAll());
console.log(userRepo.findById(1));

Zusammenfassung

  • Properties müssen deklariert werden
  • Parameter Properties für kürzeren Code
  • readonly für unveränderliche Properties
  • Getter/Setter für berechnete Properties
  • static für Klassen-Level Members
  • implements für Interface-Contracts

Im nächsten Kapitel lernst du Access Modifiers!

Zurück zum TypeScript Kurs