TypeScript im Web3-Stack: Warum wir darauf setzen
Letzten Monat habe ich zwei Stunden meines Lebens damit verbracht, einen Bug zu suchen, der sich am Ende als Tippfehler herausstellte. Der Frontend-Code rief eine Smart-Contract-Funktion auf, übergab die Parameter in der falschen Reihenfolge, und die Transaktion schlug stillschweigend fehl. Kein Fehler in der Konsole, keine rote Linie im Editor — einfach nichts. Der JavaScript-Code war syntaktisch korrekt. Er war nur semantisch falsch, und niemand hatte es bemerkt, bis ein Nutzer sich beschwerte, dass seine Tokens verschwunden waren. (Sie waren nicht verschwunden — sie steckten in einem Contract fest, weil ein uint256-Parameter den Wert einer Adresse erhalten hatte.)
Dieser Vorfall war der letzte Anstoß für eine Entscheidung, die wir eigentlich schon lange hätten treffen sollen: Unser gesamter Web3-Stack läuft jetzt auf TypeScript. Und nachdem wir seit fast einem Jahr damit arbeiten, kann ich mit Überzeugung sagen: Es war eine der besten Entscheidungen, die wir getroffen haben.
Das Problem mit JavaScript im Web3-Kontext
JavaScript ist eine großartige Sprache — flexibel, allgegenwärtig, mit einem riesigen Ökosystem. Aber genau diese Flexibilität wird im Web3-Kontext zum Problem. Wenn Sie mit Smart Contracts interagieren, arbeiten Sie mit streng typisierten Daten: uint256, address, bytes32, bool. Ein Solidity-Contract unterscheidet messerscharf zwischen einer Zahl und einer Adresse. JavaScript macht das nicht. Für JavaScript ist alles entweder ein String, eine Number oder ein Object — und die Konvertierung passiert implizit und oft auf überraschende Weise.
Das führt zu einer ganzen Klasse von Bugs, die schwer zu finden sind. Sie übergeben einen String, wo ein BigNumber erwartet wird. Sie vergessen, einen Wert in Wei umzurechnen. Sie verwechseln die Reihenfolge von Parametern in einem Contract-Call. All das sind Fehler, die zur Laufzeit auftreten — also genau dann, wenn ein echter Nutzer echtes Geld bewegt. Im besten Fall schlägt die Transaktion fehl. Im schlimmsten Fall geht sie durch, aber macht etwas anderes als beabsichtigt.
Typsicherheit für Contract-ABIs
Der größte Gewinn von TypeScript im Web3-Stack ist die Typsicherheit für Contract-Interaktionen. Tools wie TypeChain oder die eingebauten Typen von viem generieren TypeScript-Interfaces direkt aus der ABI eines Smart Contracts. Das bedeutet: Wenn Ihr Contract eine Funktion transfer(address to, uint256 amount) hat, dann kennt Ihr Editor die exakten Parameter-Typen. Sie können keinen String übergeben, wo eine Adresse erwartet wird. Sie können die Parameter nicht vertauschen. Der Compiler fängt den Fehler ab, bevor Sie auch nur an eine Transaktion denken.
In der Praxis sieht das so aus: Sie importieren die generierten Typen, und ab diesem Moment hat jeder Contract-Call Autocomplete. Sie sehen sofort, welche Funktionen verfügbar sind, welche Parameter sie erwarten und welche Rückgabewerte sie liefern. Das klingt nach einem kleinen Komfortgewinn, ist aber in Wirklichkeit ein fundamentaler Shift: Statt die ABI-Dokumentation zu lesen und zu hoffen, dass Sie alles richtig abtippen, lassen Sie den Compiler die Arbeit machen.
DX-Verbesserungen im Alltag
Jenseits der Typsicherheit verbessert TypeScript die Developer Experience auf eine Weise, die sich in reinen Zahlen schwer messen lässt, aber im Alltag enorm spürbar ist. Code-Navigation wird trivial: „Zeige mir alle Stellen, an denen diese Contract-Funktion aufgerufen wird“ ist ein Klick. Refactoring wird sicher: Wenn Sie einen Parameter umbenennen, findet der Compiler alle Stellen, die aktualisiert werden müssen. Und Onboarding neuer Teammitglieder wird schneller, weil die Typen als lebende Dokumentation dienen.
Besonders bei der Arbeit mit Libraries wie ethers.js v6 oder viem zeigt sich der Vorteil. Beide Libraries sind von Grund auf in TypeScript geschrieben und bieten hervorragende Typ-Definitionen. Wenn Sie zum Beispiel einen Provider-Call machen, wissen die Typen, dass getBlock() ein Block-Objekt zurückgibt, mit typisierten Feldern für timestamp, gasUsed und so weiter. Kein Raten mehr, kein console.log, um herauszufinden, welche Felder ein Objekt hat.
Unsere Erfahrung nach einem Jahr
Seit wir auf TypeScript umgestiegen sind, hat sich die Zahl der Laufzeitfehler in unseren dApp-Frontends deutlich reduziert. Nicht auf null — TypeScript ist kein Allheilmittel — aber die Art der Fehler hat sich verändert. Wir fangen jetzt die „dummen“ Fehler (falsche Parameter-Typen, fehlende Felder, ungültige Adressen) zur Compile-Zeit ab und können unsere Debugging-Zeit für die wirklich schwierigen Probleme verwenden: Race Conditions, Netzwerk-Timeouts, unerwartet hohe Gas-Kosten.
Der Umstieg war nicht schmerzfrei. Die initiale Konfiguration von tsconfig.json für ein Web3-Projekt erfordert ein paar Entscheidungen (insbesondere bei BigInt-Support und target-Einstellungen). Und es gibt Momente, in denen TypeScripts Typ-System im Weg steht — besonders bei dynamischen ABI-Aufrufen oder wenn Sie mit schlecht typisierten Third-Party-Libraries arbeiten. In diesen Fällen greifen wir gelegentlich zu as unknown as T, aber wir versuchen, das auf ein Minimum zu beschränken.
Fazit: Lohnt sich der Umstieg?
Wenn Sie ein Web3-Projekt starten: ja, absolut. Wenn Sie ein bestehendes JavaScript-Projekt migrieren: es kommt darauf an. Die Migration ist Aufwand, und sie bringt kurzfristig keine neuen Features. Aber langfristig zahlt sie sich aus — in weniger Bugs, schnellerem Onboarding und einer Codebase, die wartbar bleibt, wenn das Team wächst.
Im Web3 bewegen wir echtes Geld mit Code. Jeder vermiedene Typ-Fehler ist potenziell eine vermiedene finanzielle Katastrophe. TypeScript macht unseren Code nicht perfekt, aber es macht die häufigsten Fehler unmöglich. Und in einer Branche, in der ein falsch übergebener Parameter Millionen kosten kann, ist das nicht nur ein Nice-to-have — es ist eine Pflicht.