TypeScript fängt die dummen Bugs ab, bevor sie die Produktion erreichen. Es rügt dich für falsch geschriebene Properties, vergessene Argumente und Zweige, die die falsche Form zurückgeben. Du korrigierst es, der Build läuft durch, und du lieferst aus. Aber statische Typen haben eine harte Grenze. Sobald der Compiler fertig ist, werden alle Annotationen entfernt. Die JavaScript-Engine, die deinen Code ausführt, hat noch nie von deinen Interfaces, deinen Branded Types oder deinen sorgfältig eingeschränkten String-Literalen gehört. Sie kennt nur Werte und die tatsächlichen Regeln der Sprache.

Ein erfolgreicher Build bedeutet keine Laufzeitsicherheit. Grüne Tests bedeuten nicht, dass die Nutzer eine stabile Anwendung sehen. Wenn dein mentales Modell an der TypeScript-Grenze endet, fliegst du blind an genau der Stelle, an der Abstürze tatsächlich passieren.

Die Illusion der Kompilierzeit

Das gesamte Typsystem von TypeScript wird während der Kompilierung gelöscht. Öffne die kompilierte JavaScript-Ausgabe eines beliebigen Projekts, und du wirst keine Spur von interface, type oder generischen Constraints finden. Sie sind lediglich ein Gerüst zur Designzeit. Der Browser oder der Node.js-Prozess führt reines JavaScript aus, und es gibt keine Garantie, dass die Werte, die durch deine Funktionen fließen, mit den Typen übereinstimmen, die du auf dem Papier deklariert hast.

Diese Lücke ist an den Rändern deines Systems am kritischsten. Netzwerkantworten, Benutzereingaben und Bibliotheken von Drittanbietern können Werte einspeisen, die deine Typen verletzen. Eine Variable, die du als strictEmail: string deklariert hast, kann zur Laufzeit immer noch eine Zahl enthalten, wenn fehlerhafte Daten über eine nicht vertrauenswürdige API einfließen. TypeScript kann deinem Code nicht bis in die Produktion folgen, um irgendetwas zu erzwingen. Die Laufzeit operiert auf einer völlig separaten Ebene, und das Vermischen dieser beiden Ebenen führt zu Fehlern, die die statische Analyse niemals erfassen wird.

Wenn Zahlen dich verraten

TypeScript sieht eine number. Die JavaScript-Engine sieht einen IEEE 754 Double-Precision-Float. Diese Unterscheidung ist harmlos, bis sie katastrophal wird.

JavaScript reserviert 64 Bit für jede Zahl, aber nur 53 Bit werden für die Mantisse verwendet. Das ergibt eine Grenze für sichere Ganzzahlen von 9.007.199.254.740.991. Alles, was größer ist, wird auf den nächsten darstellbaren Wert gerundet. In der Praxis können zwei tatsächlich unterschiedliche Identifikatoren innerhalb deiner Anwendung zu demselben Wert kollabieren.

Snowflake-IDs und andere verteilte 64-Bit-Ganzzahl-Identifikatoren überschreiten routinemäßig dieses Limit. Finanzsysteme, die hohe Beträge in kleineren Währungseinheiten verfolgen, können ebenfalls an diese Grenze stoßen. Die Gefahr tritt oft auf, noch bevor deine Geschäftslogik überhaupt ausgeführt wird: JSON.parse konvertiert numerische Literale in einem Payload bereitwillig in JavaScript-Zahlen und kürzt dabei bei der Ankunft stillschweigend die Präzision. Deine Typdefinition mag id: number versprechen, aber der Wert zur Laufzeit ist bereits korrumpiert, noch vor dem ersten Funktionsaufruf.

Die Lösung ist unkompliziert, erfordert aber Disziplin über deinen gesamten Stack hinweg. Behalte große Identifikatoren während des Netzwerktransports als Strings bei. Definiere diese Felder in deinen JSON-Schemas und API-Verträgen als Strings, nicht als Zahlen. Wenn du Berechnungen mit Werten außerhalb des sicheren Bereichs durchführen musst, greife zu BigInt. Sei jedoch vorsichtig: BigInt lässt sich nicht implizit mit Standard-JavaScript-Zahlen mischen, und JSON.stringify kann einen BigInt nicht serialisieren, ohne einen Fehler zu werfen, es sei denn, du konvertierst ihn explizit zuerst zurück in einen String. Behandle IDs standardmäßig als opake Token. Konvertiere sie nur dann in eine numerische Form, wenn du dich innerhalb eines isolierten Berechnungsmoduls befindest, das dies wirklich benötigt