GitHub
teilweisepackages/assert/src

Assert

@ralphschuler/assert

Kleine Assertion-Funktionen machen Laufzeitbedingungen und TypeScript-Narrowing zu einem gemeinsamen Vertrag.

type narrowinginvariantserrors

01 · Problem

Wofür braucht man das?

Statische Typen schützen nicht vor Daten aus JSON, Netzwerk oder falsch aufgerufenen APIs. Kritische Invarianten müssen an der Systemgrenze zur Laufzeit erzwungen werden.

02 · Denkmodell

Das mentale Modell

Eine Assertion ist ein Gate: Hinter ihr darf der Rest des Programms von einer nachweislich erfüllten Bedingung ausgehen, andernfalls endet der Pfad mit einem klaren Fehler.

Im Repository

Spezialisierte Prüfer delegieren an assert und werfen AssertionError. Die vorhandene throws-Funktion schluckt ihren eigenen Fehler; isDefined und isNotNull versprechen typseitig mehr als sie prüfen.

03 · Kontrollfluss

Was passiert in welcher Reihenfolge?

  1. Unbekannten Wert an einer klaren Eingangsgrenze empfangen.
  2. Genau eine überprüfbare Bedingung auswerten.
  3. Bei Fehlschlag einen spezifischen AssertionError mit Kontext werfen.
  4. Bei Erfolg den Typ für nachfolgenden Code verengen.

04 · Bauteile

Die entscheidenden Verträge

assert(condition, message) Basiskontrolle, auf der alle spezialisierten Assertions aufbauen.
isString / isNumber / isDefined Laufzeitprüfungen mit TypeScript-Assertion-Signaturen.
throws(action) Soll erwartetes Werfen prüfen, besteht im Repository aber auch dann, wenn action nichts wirft.
AssertionError Unterscheidbarer Fehlertyp für verletzte Vorbedingungen.

05 · Build it yourself

Selbst implementieren

Beginne mit einem korrekten Basistor und ergänze nur Assertions, deren Runtime-Prüfung exakt zur Type-Narrowing-Aussage passt.

  1. Implementiere assert mit einer asserts-condition-Signatur.
  2. Gib jeder spezialisierten Prüfung eine präzise Fehlermeldung und einen passenden Narrowing-Typ.
  3. Teste positive und negative Pfade getrennt; fange niemals den eigenen AssertionError versehentlich ab.
minimal.ts · unabhängig vom Package
class AssertionError extends Error {}

function assert(value: unknown, message = "Assertion failed"): asserts value {
  if (!value) throw new AssertionError(message);
}

function isDefined<T>(value: T): asserts value is Exclude<T, undefined> {
  assert(value !== undefined, "Expected a defined value");
}

function throws(action: () => unknown) {
  let didThrow = false;
  try { action(); } catch { didThrow = true; }
  assert(didThrow, "Expected action to throw");
}

06 · Verifizieren

Was du testen solltest

  • Jede Assertion besteht für gültige Werte und wirft den erwarteten Fehlertyp für ungültige.
  • NaN, Infinity, null und undefined werden entsprechend des dokumentierten Vertrags behandelt.
  • throws und eine optionale rejects-Variante schlagen zuverlässig beim falschen Kontrollfluss fehl.

07 · Grenzen

Kompromisse und Stolperfallen

  • Assertions werfen synchron; für erwartbare Validierungsfehler kann ein Result-Typ geeigneter sein.
  • Zu allgemeine Assertions verschleiern den eigentlichen Eingabevertrag.
  • Strukturelle Objektvalidierung benötigt Pfadkontext und meist ein Schema statt vieler Einzelprüfungen.
Wichtig

Eine Assertion-Signatur ist ein Versprechen an den TypeScript-Compiler. Prüft die Runtime weniger, entsteht hinter dem Gate falsche Typsicherheit.

08 · Weiterdenken

Quellcode und Nachbarn

Originalcode auf GitHub ansehen