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.
Prüft Laufzeitbedingungen mit dazu passendem TypeScript-Narrowing. Assertions werfen synchron; verschachtelte Schemas und asynchrone Reject-Prüfungen sind eigene Erweiterungen.
03 · Kontrollfluss
Was passiert in welcher Reihenfolge?
- Unbekannten Wert an einer klaren Eingangsgrenze empfangen.
- Genau eine überprüfbare Bedingung auswerten.
- Bei Fehlschlag einen spezifischen AssertionError mit Kontext werfen.
- Bei Erfolg den Typ für nachfolgenden Code verengen.
04 · Bauteile
Die entscheidenden Verträge
assert / isString / isNumberPrüft Laufzeitbedingungen und verengt Typen.isDefined / isNotNullSchließt genau undefined beziehungsweise null aus.throws / AssertionErrorPrüft synchrones Werfen mit unterscheidbarem Fehlertyp.05 · Selbst bauen
Selbst implementieren
Beginne mit einem korrekten Basistor und ergänze nur Assertions, deren Runtime-Prüfung exakt zur Type-Narrowing-Aussage passt.
- Implementiere assert mit einer asserts-condition-Signatur.
- Gib jeder spezialisierten Prüfung eine präzise Fehlermeldung und einen passenden Narrowing-Typ.
- Teste positive und negative Pfade getrennt; fange niemals den eigenen AssertionError versehentlich ab.
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 solltest du testen?
- 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.
Selbst ausprobieren
Formuliere die erste Prüfaussage als fehlschlagenden Test, implementiere die kleinste Korrektur und ergänze einen Grenzfall.
Jede Assertion besteht für gültige Werte und wirft den erwarteten Fehlertyp für ungültige.
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
- 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.
Assertions werfen synchron; verschachtelte Schemas und asynchrone Reject-Prüfungen sind eigene Erweiterungen.
08 · Weiterdenken
Quellcode und verwandte Ideen
yarn tsx packages/assert/examples/basic.ts