Funktionale Programmierung in TypeScript: Fortgeschrittenes Niveau

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Funktionale Programmierung in TypeScript: Fortgeschrittenes Niveau

Funktionale Programmierung (FP) ist ein Paradigma, das in den letzten Jahren die Welt von JavaScript und TypeScript aktiv erobert. Für Entwickler, die bereits mit den Grundlagen vertraut sind (reine Funktionen, Unveränderlichkeit von Daten), eröffnet sich eine neue Ebene von Möglichkeiten: die Arbeit mit Monaden, Funktoren, Applikativen und komplexen Kompositionen. In diesem Artikel werden wir fortgeschrittene FP-Konzepte in TypeScript mit praktischen Beispielen behandeln.



1. Unveränderlichkeit und strukturelle gemeinsame Nutzung von Daten

Unveränderlichkeit (Immutability) ist ein Eckpfeiler der FP. In TypeScript können wir Unveränderlichkeit auf Typebene mit readonly und ReadonlyArray garantieren. Für effizientes Arbeiten mit großen Datenstrukturen (z.B. Redux Store) wird jedoch strukturelle gemeinsame Nutzung (structural sharing) verwendet – Bibliotheken wie Immer oder Immutable.js.

Betrachten wir ein Beispiel mit readonly und einer Funktion zum Aktualisieren eines verschachtelten Objekts ohne Mutation:

type User = {  readonly id: number;  readonly name: string;  readonly address: {    readonly city: string;    readonly zip: string;  };};

const updateUserCity = (user: User, newCity: string): User => ({ ...user, address: { ...user.address, city: newCity, },});

const user: User = { id: 1, name: 'Alice', address: { city: 'NYC', zip: '10001' } };const updatedUser = updateUserCity(user, 'LA');console.log(user.address.city); // NYC (Original wurde nicht geändert)console.log(updatedUser.address.city); // LA

Beachten Sie: Wir erstellen ein neues Objekt, verwenden aber unveränderte Teile (z.B. id und name) wieder. Dies ist manuelles structural sharing. Für tiefe Verschachtelungen ist es besser, Bibliotheken zu verwenden.



2. Funktoren und ihre Implementierung

Ein Funktor ist ein Container, der die Methode map implementiert, welche es ermöglicht, eine Funktion auf den Inhalt des Containers anzuwenden, ohne dessen Struktur zu verletzen. In TypeScript ist ein typischer Funktor Array. Wir können aber auch unseren eigenen Funktor erstellen, z.B. Maybe, um Fehler mit null und undefined zu vermeiden.

Wir implementieren einen einfachen Funktor Maybe:

class Maybe<T> {  private constructor(private value: T | null) {}

static of<U>(val: U | null | undefined): Maybe<U> { return new Maybe(val ?? null); }

map<U>(fn: (val: T) => U): Maybe<U> { if (this.value === null) { return Maybe.of<U>(null); } return Maybe.of(fn(this.value)); }

getOrElse(defaultValue: T): T { return this.value !== null ? this.value : defaultValue; }}

// Verwendungconst result = Maybe.of('hello') .map(s => s.toUpperCase()) .map(s => s + ' world') .getOrElse('fallback');console.log(result); // "HELLO WORLD"

const empty = Maybe.of<string>(null) .map(s => s.toUpperCase()) .getOrElse('default');console.log(empty); // "default"

Der Funktor Maybe ermöglicht es, Funktionen sicher zu verketten, ohne sich um null sorgen zu müssen. Jeder map-Aufruf gibt einen neuen Maybe zurück und bewahrt den Kontext.



3. Monaden: Ketten von Operationen mit Seiteneffekten

Eine Monade ist ein Funktor, der auch die Methode flatMap (oft bind oder chain genannt) implementiert. Sie ermöglicht das "Entfalten" von verschachtelten Containern. Ein klassisches Beispiel für eine Monade ist Promise oder Array. Betrachten wir die Monade Either zur Fehlerbehandlung ohne Ausnahmen.

type Either<L, R> = Left<L> | Right<R>;

class Left<L> { readonly value: L; constructor(value: L) { this.value = value; } isLeft(): this is Left<L> { return true; } isRight(): this is Right<ne

Blogs

Buchempfehlungen