GitHub
Prototyppackages/functional/src

Functional

@ralphschuler/functional

Modelliert Abwesenheit und Fehler als explizite Werte, damit Kontrollfluss typisiert und exhaustiv bleibt.

OptionResultdiscriminated unions

01 · Problem

Wofür braucht man das?

null, undefined und Exceptions verstecken alternative Pfade. Option und Result machen diese Pfade zu sichtbaren Varianten im Typ.

02 · Denkmodell

Das mentale Modell

Jeder Wert trägt einen eindeutigen Tag. Consumer müssen vor dem Zugriff die Variante prüfen oder alle Varianten durch einen Matcher behandeln.

Im Repository

Option, Result, Konstruktoren, Guards und match sind vollständig intern und werden nicht exportiert. Übliche Kombinatoren fehlen; der Matcher mischt Option- und Result-Zweige.

03 · Kontrollfluss

Was passiert in welcher Reihenfolge?

  1. Erfolg, Fehler oder Abwesenheit als unveränderliche getaggte Variante erzeugen.
  2. Mit dem Tag verengen statt Felder oder Truthiness zu erraten.
  3. Transformationen nur auf der passenden Erfolgsvariante anwenden.
  4. Am Systemrand exhaustiv in Ausgabe, HTTP-Status oder Exception übersetzen.

04 · Bauteile

Die entscheidenden Verträge

Option = Some | None (intern) Explizite Anwesenheit eines Werts.
Result = Ok | Err (intern) Expliziter Erfolg oder fachlicher Fehler.
isSome / isOk / match (intern) Type Guards und Pattern-Matching-Helfer ohne Package-Export.

05 · Build it yourself

Selbst implementieren

Discriminated Unions benötigen kaum Runtime-Code; die Qualität entsteht aus präzisen Kombinatoren und exhaustiven Switches.

  1. Definiere separate Tags und Payload-Felder für jede Variante.
  2. Baue kleine Konstruktoren sowie map und flatMap für Erfolgspfade.
  3. Nutze never-Checks, damit neue Varianten Compilerfehler an unvollständigen Matchern erzeugen.
minimal.ts · unabhängig vom Package
type Result<T, E> =
  | { tag: "ok"; value: T }
  | { tag: "err"; error: E };

const ok = <T>(value: T): Result<T, never> => ({ tag: "ok", value });
const err = <E>(error: E): Result<never, E> => ({ tag: "err", error });

function map<A, B, E>(result: Result<A, E>, fn: (value: A) => B): Result<B, E> {
  return result.tag === "ok" ? ok(fn(result.value)) : result;
}

06 · Verifizieren

Was du testen solltest

  • map verändert ausschließlich Ok/Some und lässt Err/None unverändert.
  • flatMap propagiert den ersten Fehler ohne den nächsten Schritt auszuführen.
  • Exhaustive Matcher werden typseitig gegen neue Varianten abgesichert.

07 · Grenzen

Kompromisse und Stolperfallen

  • Result-Werte müssen konsequent behandelt werden; gemischte Exception- und Result-Semantik wird schnell unübersichtlich.
  • Zu viele generische Kombinatoren können TypeScript-Inferenz und Fehlermeldungen verschlechtern.
Wichtig

Das vorhandene Modul zeigt die Kernidee, besitzt aber derzeit keine öffentliche API.

08 · Weiterdenken

Quellcode und Nachbarn

Originalcode auf GitHub ansehen