EnglishGitHub
teilweisepackages/functional/src

Functional

@ralphschuler/functional

Exportiert markierte Option- und Result-Werte, Guards und Pattern Matching.

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

Exportiert markierte Option- und Result-Werte, Guards und Pattern Matching. Hüllen sind flach eingefroren; Fehlerfang und höhere Kombinatoren bleiben explizite Erweiterungen.

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

Some / None / Ok / ErrErzeugt unveränderliche markierte Hüllen.
isSome / isNone / isOk / isErr / matchVerengt und verarbeitet die jeweiligen Varianten.

05 · Selbst bauen

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.
Konzeptskizze · kann ausgelassene Helfer verwenden
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 solltest du testen?

  • 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.

Selbst ausprobieren

Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.

map verändert ausschließlich Ok/Some und lässt Err/None unverändert.

Ansatz vergleichen

Dein Test sollte beim gezielten Verletzen der Invariante fehlschlagen und nach der Korrektur wieder bestehen. Halte den Aufbau deterministisch und prüfe ein beobachtbares Ergebnis.

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

Hüllen sind flach eingefroren; Fehlerfang und höhere Kombinatoren bleiben explizite Erweiterungen.

08 · Weiterdenken

Quellcode und verwandte Ideen

yarn tsx packages/functional/examples/basic.ts