Funktionale Programmierung in TypeScript: von A bis Z
Funktionale Programmierung (FP) ist ein Paradigma, das Berechnungen als Auswertung mathematischer Funktionen betrachtet und Zustandsänderungen sowie veränderliche Daten vermeidet. TypeScript, als streng typisierte Erweiterung von JavaScript, bietet eine hervorragende Grundlage für die Anwendung von FP-Prinzipien und ermöglicht es, vorhersagbareren, testbareren und zuverlässigeren Code zu schreiben. In diesem Artikel werden wir die wichtigsten Konzepte der FP in TypeScript analysieren, von den Grundlagen bis hin zu fortgeschrittenen Techniken.
Grundprinzipien der funktionalen Programmierung
Bevor wir uns in den Code vertiefen, ist es wichtig, die fundamentalen Prinzipien zu verstehen, auf denen FP aufbaut.
Reine Funktionen (Pure Functions)
Eine reine Funktion ist eine Funktion, die:
- Für dieselben Argumente immer dasselbe Ergebnis zurückgibt (Determinismus).
- Keine Seiteneffekte hat (keine externen Variablen ändert, keine Daten in die Konsole ausgibt, keine Anfragen sendet).
Beispiel einer reinen Funktion:
function add(a: number, b: number): number { return a + b;}
// Immer vorhersagbarconsole.log(add(2, 3)); // 5console.log(add(2, 3)); // 5Beispiel einer unreinen Funktion:
let counter = 0;function increment(): number { return ++counter; // Seiteneffekt: Änderung einer externen Variable}Unveränderlichkeit (Immutability)
Daten sollten nach ihrer Erstellung nicht verändert werden. Statt ein bestehendes Objekt zu ändern, erstellen wir eine neue Kopie mit den notwendigen Änderungen.
interface User { name: string; age: number;}
const user: User = { name: "Alice", age: 30 };
// Schlecht (Mutation)user.age = 31;
// Gut (Unveränderlichkeit)const updatedUser: User = { ...user, age: 31 };
console.log(user); // { name: "Alice", age: 30 } - Original wurde nicht geändertconsole.log(updatedUser); // { name: "Alice", age: 31 }Komposition und Currying in TypeScript
Funktionskomposition ist der Prozess der Kombination mehrerer einfacher Funktionen, um eine komplexere zu erstellen. Currying ist die Transformation einer Funktion mit mehreren Argumenten in eine Folge von Funktionen, die jeweils ein Argument entgegennehmen.
Komposition (Function Composition)
Typisierte Implementierung von Komposition in TypeScript:
function compose<A, B, C>(f: (x: B) => C, g: (x: A) => B): (x: A) => C { return (x: A) => f(g(x));}
const toUpperCase = (str: string): string => str.toUpperCase();const exclaim = (str: string): string => `${str}!`;
const shout = compose(exclaim, toUpperCase);console.log(shout("hello world")); // "HELLO WORLD!"Currying
Beispiel einer curryierten Funktion:
function multiply(a: number): (b: number) => number { return (b: number) => a * b;}
const double = multiply(2);const triple = multiply(3);
console.log(double(5)); // 10console.log(triple(5)); // 15Currying macht Funktionen flexibler und bequemer für die Komposition.
Arbeit mit Monaden: Option und Result
Monaden sind ein mächtiges Pattern für die Arbeit mit Seiteneffekten und Fehlerbehandlung, ohne die Reinheit von Funktionen zu verletzen. In TypeScript werden häufig die Monaden Option (oder Maybe) und Result (oder Either) verwendet.
Monade Option (Maybe)
Ermöglicht sicheres Arbeiten mit Werten, die null oder undefined sein können.
type Option<T> = T | null;
function safeDivide(a: number, b: number): Option<number> { return b === 0 ? null : a / b;}
function getValue(value: Option<number>): string { if (value === null) { return "Fehler: Division durch Null"; } return `Ergebnis: ${value}`;}
console.log(getValue(safeDivide(10, 2)