Figmas Weg zur Umstellung auf TypeScript
(figma.com)- Figma hat seinen gesamten Skew-Code nach TypeScript migriert, nachdem die eigene Sprache Skew, die für mobiles Rendering und den Prototyp-Viewer verwendet wurde, bei Onboarding, Integration und Ökosystem an Grenzen gestoßen war
- Die Voraussetzungen für die Umstellung entstanden durch die breitere WebAssembly-Unterstützung in mobilen Browsern, den Ersatz zentraler Codepfade durch eine C++-Engine sowie das Wachstum der Prototyping- und Mobile-Teams
- Die Migration verlief in drei Phasen:
Write Skew, build Skew→Write Skew, build TypeScript→Write TypeScript, build TypeScript; ein Skew-to-TypeScript-Transpiler hielt den Entwicklungsfluss aufrecht - In der tatsächlichen Migration traten Kompatibilitätsprobleme zutage, etwa bei der Performance von Array-Destructuring, Unterschieden bei Devirtualisierung (devirtualization), Initialisierungsreihenfolge, Source-Map-Verknüpfung und fehlender bedingter Kompilierung
- Am Ende verfügte Figma über eine TypeScript-Codebasis, die Unit-Tests bestand und eine ähnliche Performance wie Skew erreichte; als nächste Aufgaben sieht Figma die Integration internen und externen Codes, Paketverwaltung und die Nutzung des TypeScript-Ökosystems
Warum der Wechsel von Skew zu TypeScript
- Skew entstand aus einem frühen Nebenprojekt bei Figma und erfüllte die Anforderung, schnell einen Prototyp-Viewer zu bauen, der sowohl Web als auch Mobile unterstützt
- Als Sprache, die nach JavaScript kompiliert, bot Skew fortgeschrittene Optimierungen und schnelle Kompilierzeiten; je mehr Code sich jedoch im Prototyp-Viewer ansammelte, desto höher wurden die Wartungskosten
- Für neue Mitarbeitende schwer zu erlernen
- Nicht einfach in den übrigen Codebestand zu integrieren
- Kein Entwickler-Ökosystem außerhalb von Figma
- Von der Umstellung auf TypeScript erwartete man höhere Entwicklungsgeschwindigkeit und einen größeren Rahmen für Zusammenarbeit
- Vereinfachte Integration internen und externen Codes durch statische Imports und natives Paketmanagement
- Nutzung von Tools aus einer großen Entwickler-Community, darunter Linter, Bundler und statische Analyzer
- Verwendung moderner JavaScript-Funktionen wie async/await und eines flexibleren Typsystems
- Niedrigere Hürden für das Onboarding neuer Entwickler und die Beteiligung anderer Teams
Bedingungen, die die Umstellung erst vor Kurzem ermöglichten
- Als Figma die mobile Codebasis erstmals erstellte, unterstützten mobile Browser WebAssembly noch nicht, und große Bundles performant zu laden war ebenfalls schwierig
- Auch TypeScript befand sich noch in einer frühen Phase, weshalb Skew mit statischen Typen und einem strengeren Typsystem damals die realistischere Wahl war
- WebAssembly erhielt bis 2018 breite Unterstützung auf Mobilgeräten, und nach Figmas Tests erreichte die mobile Performance 2020 ein verlässliches Niveau
- Die frühen Stärken von Skew lagen in klassischen Compiler-Optimierungen wie Constant Folding und Devirtualisierung sowie in webspezifischen Optimierungen wie der Erzeugung von JavaScript mit echten Integer-Operationen
- Benchmarks aus dem Jahr 2020 zeigten, dass das Laden eines Figma-Prototyps mit TypeScript in Safari fast 2-mal langsamer sein konnte; dass Safari unter iOS die einzige erlaubte Browser-Engine war, blockierte die Umstellung zusätzlich
- Mit iOS 17.4 öffnete Apple für Nutzer in der EU andere Browser-Engines, außerhalb dieser Regionen bleibt WebKit jedoch weiterhin die einzige Browser-Engine
- Später wurden viele zentrale Komponenten der Skew-Engine durch entsprechende Komponenten der C++-Engine ersetzt, wodurch das Performance-Risiko einer TypeScript-Migration sank
- Darunter fielen die heißesten Codepfade wie das Laden von Dateien
- Figma gewann die Zuversicht, dass die Performance-Einbußen bei einem Wechsel zu TypeScript tragbar wären
- Als die Prototyping- und Mobile-Teams zu größeren Organisationen heranwuchsen, konnten Ressourcen in automatische Migration und Verbesserungen der Developer Experience investiert werden
Umstellung der Codebasis in drei Phasen
- Im ersten Migrationsprototyp von 2020 war TypeScript in der Performance fast 2-mal langsamer
- Nachdem WebAssembly-Unterstützung und die C++-Umstellung der Mobile-Engine weit genug fortgeschritten waren, demonstrierte Figma während der Maker Week eine funktionierende Migration, indem ein bestehender Prototyp so angepasst wurde, dass alle Tests bestanden
- Ziel war es, die gesamte Codebasis auf TypeScript umzustellen; ein manuelles Neuschreiben hätte jedoch das Risiko geringerer Entwicklungsgeschwindigkeit, von Laufzeitfehlern und Performance-Verlusten geschaffen
- Zwischen Skew und TypeScript gab es größere semantische Unterschiede als bei einem einfachen Wechsel zwischen Sprachen der Kategorie „JavaScript mit Typen“
- In TypeScript werden Namespaces und Klassen erst initialisiert, nachdem eine Datei importiert wurde
- Wenn die Importreihenfolge nicht den Erwartungen entspricht, können Laufzeitfehler auftreten
- Skew macht beim Laden alle Symbole zur Laufzeit für die gesamte Codebasis verfügbar
- Auf Basis einer Arbeit, die Evan Wallace früher begonnen hatte, entwickelte Figma einen Skew-to-TypeScript-Transpiler
-
Phase 1: Skew schreiben und Skew bauen
- Der Transpiler wurde entwickelt, während der bestehende Build-Prozess beibehalten wurde
- Der generierte TypeScript-Code wurde in GitHub eingecheckt, damit Entwickler die Form der neuen Codebasis sehen konnten
-
Phase 2: Skew schreiben und TypeScript bauen
- Nachdem ein TypeScript-Bundle erzeugt wurde, das alle Unit-Tests bestand, rollte Figma Produktions-Traffic schrittweise auf Builds der TypeScript-Codebasis aus
- Entwickler schrieben weiterhin Skew; der Transpiler wandelte Skew-Code in TypeScript um und aktualisierte den TypeScript-Code auf GitHub
- Auch Typfehler im generierten Code wurden laufend behoben
- TypeScript konnte auch bei Typfehlern ein gültiges Bundle erzeugen
-
Phase 3: TypeScript schreiben und TypeScript bauen
- Nachdem alle Nutzer den TypeScript-Build-Prozess durchlaufen hatten, wurde TypeScript-Code zur einzigen Quelle der Wahrheit für die Entwicklung
- Figma suchte einen Zeitpunkt ohne Code-Merges, stoppte den automatischen Generierungsprozess und entfernte den Skew-Code aus der Codebasis
- Am Freitagabend wurden die Entfernung der automatischen Generierung und die Umstellung der CI-Jobs auf das direkte Ausführen von TypeScript-Dateien gemergt
- Während des schrittweisen Rollouts wurde intern ein Defekt an der Smart-Animate-Funktion entdeckt; dank des gated Rollouts konnte die Einführung deaktiviert, der Fehler behoben und der Plan neu bewertet werden
Probleme, die im Transpiler sichtbar wurden
- Compiler bestehen üblicherweise aus Frontend und Backend
- Das Frontend parst den Eingabecode, führt Typ- und Syntaxprüfungen durch und wandelt ihn in eine Intermediate Representation (IR) um
- Das Backend wandelt die IR in eine andere Sprache um
- Das Backend des Skew-Compilers erzeugt obfuskierten und minifizierten JavaScript-Code
- Ein Transpiler ist ein spezieller Compiler, dessen Backend menschenlesbaren Code erzeugt; Figma musste aus der Skew-IR menschenlesbares TypeScript erzeugen
- Die erste Implementierung ließ sich stark vom JavaScript-Backend von Skew inspirieren und war vergleichsweise unkompliziert, doch in der späteren Phase traten mehrere schwer nachzuverfolgende und zu behandelnde Probleme auf
-
Performance von Array-Destructuring
- Bei der Untersuchung des Offline-Performance-Unterschieds zwischen Skew und TypeScript an einem Beispielprototyp zeigte TypeScript eine niedrigere Framerate
- Ursache war das Array-Destructuring in JavaScript
- Bei Operationen wie
const [a, b] = function_that_returns_an_array()indiziert JavaScript das Array nicht direkt, sondern erstellt einen Iterator und durchläuft ihn - Figma nutzte diese Methode, um Argumente aus dem JavaScript-Schlüsselwort
argumentszu holen, was in einem bestimmten Testfall langsam wurde - Durch generierten Code, der statt Destructuring das arguments-Array direkt indiziert, verbesserte sich die Latenz pro Frame um bis zu 25 %
-
Skews Optimierung zur Devirtualisierung
- Der Skew-Compiler führt unter bestimmten Bedingungen eine devirtualization-Optimierung durch, bei der Funktionen innerhalb einer Klasse herausgezogen und zu globalen Funktionen angehoben werden
myObject.myFunc(a, b)kann zumyFunc(myObject, a, b)werden- TypeScript nimmt diese Optimierung nicht vor
- Der Defekt in Smart Animate trat auf, wenn
myObjectnull war - Der devirtualisierte Aufruf lief korrekt, während der nicht devirtualisierte Aufruf eine Ausnahme wegen Nullzugriff auslöste
- Um weitere problematische Aufrufstellen zu finden, fügte Figma Logging zu allen Funktionen hinzu, die an Devirtualisierung teilnehmen konnten
- Nach kurzer Aktivierung des Loggings wurden Produktionslogs analysiert und die problematischen Aufrufstellen behoben
-
Unterschiede bei der Initialisierungsreihenfolge
- In Skew spielt es keine Rolle, an welcher Stelle Variablen, Klassen, Namespaces oder Funktionsdefinitionen deklariert werden
- In TypeScript ist die Initialisierungsreihenfolge globaler Variablen oder Klassendefinitionen wichtig
- Wird eine statische Klassenvariable vor der Klassendefinition initialisiert, entsteht ein Compile-Time-Fehler
- Der frühe Transpiler verwendete keine Namespaces und flachte alle Funktionen in den globalen Scope ab, um ein Verhalten ähnlich wie Skew beizubehalten
- Der resultierende Code war schwer lesbar; später wurde der Transpiler angepasst, um TypeScript-Code in der richtigen Reihenfolge auszugeben und Namespaces zur besseren Lesbarkeit wieder einzuführen
- Am Ende entstand ein Transpiler, der Unit-Tests bestand und TypeScript-Code kompilieren konnte, dessen Performance zu Skew passte
- Einige kleinere Probleme wurden nicht durch neue Änderungen am Transpiler behoben, sondern manuell im ursprünglichen Skew-Code oder nach der Umstellung auf TypeScript korrigiert
Debugging-Erfahrung mit Source Maps beibehalten
- Figma legte während der TypeScript-Umstellung großen Wert auf Source Maps, damit Entwickler ohne Unterbrechung debuggen konnten
- Browser-Debugger verstehen nur JavaScript, Entwickler setzen Breakpoints jedoch im Skew- oder TypeScript-Quellcode
- Source Maps verbinden Positionen im kompilierten JavaScript mit Positionen im ursprünglichen Quellcode
- Beispielsweise sind Mappings wie
helper → c,myInt → a,arrayOfInts → bmöglich
- Beispielsweise sind Mappings wie
- Üblicherweise ist eine einzelne Source-Map-Datei mit der Erweiterung
.mapmit dem finalen JavaScript-Bundle verbunden - Die bestehende Infrastruktur erzeugte Source Maps von Skew nach JavaScript; in Phase 2 änderte sich die Pipeline jedoch zum Ablauf Skew → TypeScript → Bundling mit esbuild
- Würde man die bestehende Source Map unverändert nutzen, wären die Mappings zwischen JavaScript- und Skew-Code verschoben, und Entwickler könnten nicht mehr debuggen
- Der neue Build-Prozess setzt Source Maps in drei Schritten zusammen
- Step 1: esbuild erzeugt die Source Map
ts-to-js.mapfür TypeScript → JavaScript - Step 2: Der Transpiler erzeugt für jede Skew-Datei eine Source Map von Skew → TypeScript
- Step 3: Beide Source Maps werden zusammengesetzt, um die finale Source Map von Skew → JavaScript zu erzeugen
- Step 1: esbuild erzeugt die Source Map
- Mit der finalen Source Map lässt sich das neue JavaScript-Bundle auf Skew-Code abbilden, sodass die Developer Experience auch in Phase 2 erhalten bleibt
Umgang mit bedingter Kompilierung in TypeScript
- Skew unterstützt bedingte Kompilierung über Top-Level-
if-Statements und über die an den Compiler übergebene Optiondefines - Damit ließen sich aus derselben Codebasis unterschiedliche Bundles erzeugen
- Das reale Bundle, das an Nutzer ausgeliefert wird
- Ein Bundle nur für Unit-Tests
- Funktionen oder Klassen, die in Debug- oder Release-Builds unterschiedliche Implementierungen verwenden
- Da TypeScript keine bedingte Kompilierung besitzt, nutzte Figma nach der Typprüfung die
defines- und Dead-Code-Elimination-Funktionen von esbuild, um bedingte Kompilierung in der Bundling-Phase durchzuführen - In diesem Ansatz können
definesdie Typprüfung nicht beeinflussen- Code wie
testOnlyFunction, der nur beiBUILD == "TEST"existiert, kann in TypeScript nicht einfach so vorhanden sein
- Code wie
- Die Lösung bestand darin, Klassen und Methoden immer zu definieren und innerhalb der Methode abhängig vom Wert von
BUILDzu verzweigen- Wird
testOnlyFunctionin einem Build aufgerufen, der nicht nur für Tests gedacht ist, wirft sie den FehlerUnexpected call to test-only function
- Wird
- Das finale JavaScript konnte so kompiliert werden, dass es dem JavaScript entsprach, das der ursprüngliche Skew-Code erzeugt hätte
- Allerdings existierten nach der Änderung einige Symbole in allen Modi, obwohl sie ursprünglich nur in bestimmten Kompilierungsmodi vorhanden waren, wodurch das finale Bundle etwas größer wurde
- In Tests war die Zunahme der Bundle-Größe akzeptabel, und nicht exportierte Top-Level-Symbole konnten durch tree-shaking entfernt werden
Richtung nach der Umstellung
- Durch die Migration des gesamten Skew-Codes zu TypeScript wurde eine zentrale Codebasis von Figma modernisiert
- Die Integration mit internem und externem Code wurde einfacher, und Entwickler können effizienter arbeiten
- Für die damaligen Anforderungen und Fähigkeiten von Figma war die Entscheidung, die Codebasis mit Skew zu beginnen, angemessen
- Mit der Reifung der Technologie ist TypeScript, das früher nicht geeignet war, heute zu einer passenden Wahl geworden
- Figma prüft für die weitere Arbeit folgende Möglichkeiten
- Integration mit dem übrigen Codebestand
- Deutlich einfacheres Paketmanagement
- Direkte Nutzung neuer Funktionen aus dem aktiven TypeScript-Ökosystem
- Während der Umstellung lernte Figma viele Aspekte von TypeScript kennen, darunter Import-Auflösung, Modulsysteme und JavaScript-Codegenerierung
1 Kommentare
Meinungen auf Hacker News
Dass Figma eine eigene Sprache für JS hatte, ist schon überraschend; noch überraschender ist, dass sie schneller war als TS.
Interessant ist aber auch, dass sie am Ende trotzdem auf das langsamere TS umgestiegen sind.
So etwas sieht man ziemlich häufig: Ein Unternehmen baut am Anfang eigene Technologie und wechselt später, wenn es größer geworden ist, zu etwas „Standardisierterem“.
Wenn Team und Produkt viel größer werden, migriert man wieder auf einen vertrauteren und weit verbreiteten Stack.
Der Erfolg einer solchen Umstellung hängt stark davon ab, wie solide die Engineering-Kultur der Organisation ist. Ich habe keine Belege, aber es wirkt so, als hätten Evan und die Gründer bei Figma eine hervorragende Engineering-Kultur aufgebaut, und als gebe es genug Resilienz, um Fehler zu korrigieren, wenn sie passieren.
Zu diesem Zeitpunkt blieb nur noch Glue Code übrig, und die eigene Sprache hatte keinen wirklichen Existenzgrund mehr.
Wenn die Community dann aufholt und eine größere Dynamik entwickelt als das Unternehmen, wird es sinnvoll, zur Standardimplementierung zu wechseln.
Den Weg von Googles Borg zu k8s sehe ich ein wenig ähnlich. Allerdings hat Google die Community eher um einen von ihnen geschaffenen Standard herum eingeladen.
Man muss die Kosten von Entwicklern vergleichen, die ein völlig neues Framework zum ersten Mal sehen und erlernen müssen, mit dem Wert von Entwicklern, die bereits dokumentierte Erfahrung mit der verwendeten Plattform oder Technologie haben.
Mit vollständiger Eigenentwicklung kann man aus jeder Sprache und jedem Framework enorme Performance herausholen. Aber je mehr Situationen ein Projekt abdecken muss, desto mehr Entwickler werden für die Wartung benötigt, und irgendwann kommt der Punkt, an dem Standardsoftware besser wird, auch wenn sie langsamer ist und nicht exakt zu den Anforderungen passt.
Wenn man langfristig Einstellungsstandards auf Unicorn-Niveau halten kann, lässt sich vielleicht auch ein komplett eigener Stack betreiben. Die meisten Organisationen müssen sich aber in Richtung dessen bewegen, was durchschnittliche Neueinstellungen künftig warten können; meistens bedeutet das Produkte bekannter, langweiliger Softwareanbieter.
Für ehrgeizige Leute kann das ein Grund sein zu gehen, Recruiting wird schwieriger, und das Onboarding dauert länger. Wenn man die eigene Sprache später aufgibt, braucht man außerdem ein großes Migrationsprojekt.
Manchmal kann es sich lohnen, wenn die Performancegewinne enorm sind, wenn man sichereren Code schreiben kann oder wenn man bewusst nur Leute einstellen will, die gern neue Sprachen lernen.
Ich habe an diesem Projekt gearbeitet. Auf Twitter habe ich noch etwas mehr dazu geschrieben: https://twitter.com/andrew_k_chan/status/1786769203912925477
Der Titel des Artikels ist irreführend. Bei Figma wurde Typescript fast zehn Jahre lang in anderen Teilen der Codebasis verwendet, und fast während dieses gesamten Zeitraums gab es mehr Typescript-Code als Skew-Code.
Wie im Blogbeitrag erklärt, wurde Skew für die Mobile Engine, später für den Prototyping-Player, die Mirroring-Funktion und ein oder zwei Produktbereiche eingesetzt, an die ich mich nicht mehr erinnere.
Skew war nicht nur ein bisschen schneller als TypeScript.
Laut Evan Wallace, dem ehemaligen CTO von Figma, war es dank Optimierungen, die durch ein strengeres Typsystem möglich wurden, 1,5- bis 2-mal schneller.
Es scheint ziemlich große Hürden gegeben zu haben, aber für produktionsreife Apps klingt es nach einem guten Kompromiss, etwas Dynamik aufzugeben, um vorhersehbarere Optimierungen zu ermöglichen.
Wenn man die heißen Ausführungspfade optimiert, verschwindet der 2-fache Vorteil mit recht hoher Wahrscheinlichkeit.
Natürlich kann man dann sagen, dass man an vielen Stellen optimiertes JS erzwingen müsste und dass das die Lesbarkeit beeinträchtigt. Das stimmt, aber die Alternative war eine völlig neue, zu JS kompilierende Sprache mit weniger Tooling-Unterstützung und geringerer Bekanntheit.
Rückblickend scheint es das nicht wert gewesen zu sein, und der Blogbeitrag wirkt ein wenig so, als würde er eine zuvor umstrittene Technologieentscheidung etwas positiver darstellen.
Ich frage mich, ob man die TypeScript-Nutzung auf die Teilmenge beschränken könnte, die diese Performance erreicht.
Interessant ist, dass JavaScript beim Abschließen einer Operation wie
const [a, b] = function_that_returns_an_array()nicht direkt in das Array indiziert, sondern einen Iterator erstellt, der das Array durchläuft.Warum indiziert JS bei Destructuring-Zuweisungen nicht einfach direkt in Arrays?
Symbol.iteratorzu einem iterierbaren Objekt werden, und Destructuring muss auch mit solchen Objekten funktionieren. SogarSymbol.iteratordes Arrays selbst kann gepatcht werden, also muss die VM damit umgehen.Array.prototype[Symbol.iterator] = function*() { yield 1; yield 2; yield 3; }[...[4, 5, 6]][1, 2, 3]Die schreckliche Performance des Iterator-Protokolls wurde schon damals diskutiert, aber mit dem Verweis ignoriert, dass Escape Analysis das lösen werde [0]. Fast zehn Jahre später hat Escape Analysis es immer noch nicht gelöst; es nutzt sehr viel GC und ist weiterhin ziemlich schlecht. Das ist eine schlechte Spezifikation, entworfen von Leuten ohne Performance-Bewusstsein.
Wenn der Nutzer
Symbol.iteratornicht gepatcht hat, könnte es sinnvoll sein, dass Engines Destructuring-Zuweisungen und Splicing von Arrays spezialisieren und den Overhead des Iterator-Protokolls eliminieren, aber das ist wieder ein anderes nerviges Problem.[0] https://esdiscuss.org/topic/performance-of-iterator-next-as-...
Noch seltsamer ist, dass in manchen JavaScript-Engines beim Destructuring von Arrays mit zwei oder drei Elementen Objekt-Destructuring der Form
{0: foo, 1: bar}schneller sein kann.Die fortlaufenden Auswirkungen auf die Developer Experience durch den Verlust der Komfortfunktionen von Skew werden kaum behandelt; es geht nur um die einmalige Transformation bei der Migration der Codebasis.
In TypeScript kann es zum Beispiel leicht passieren, dass Dateien in der richtigen Reihenfolge importiert werden müssen, sonst kann etwas kaputtgehen. Außerdem ist Destructuring langsam und sollte nicht verwendet werden, sobald Performance auch nur ein bisschen wichtig ist.
Nachdem ich TypeScript einige Jahre genutzt habe, gibt es Dutzende solcher Fallstricke; einige stammen aus JavaScript, andere nicht. Wenn man viele Engineers hat, braucht man zumindest einen sehr umfangreichen Styleguide.
Ich frage mich, ob es Engineers gab, die bedauert haben, dass Skew verschwindet.
So konnten Entwickler in PR-Reviews sehen, wie das Ergebnis aussehen würde, und Probleme melden.
Bei Performance und Laufzeit-Korrektheit stimmt es, dass TS einige Fallstricke hat. Probleme wie Array-Destructuring haben wir durch Instrumentation und strenges Monitoring gefunden.
Einige Funktionen von Skew, etwa Operator Overloading und Integer-Typen, zu verlieren, war definitiv schade. Aber die Migration war letztlich eine Entscheidung des gesamten Teams, und ich denke ebenfalls, dass es die richtige Entscheidung war.
Da steht „moderne JavaScript-Funktionen wie async/await und ein flexibleres Typsystem“ – hatte Skew dann also nur Callbacks?
Promise?Aus der Perspektive von jemandem, der Figma nicht gut kennt: Warum verwenden sie WebAssembly?
Figma macht das extrem gut. Selbst auf sehr großen Leinwänden mit vielen komplexen UI-Screens ist es unglaublich reaktionsschnell.
Selbst unter heutigen Desktop-Anwendungen gibt es kaum etwas, das so gut läuft. Ich bin überzeugt, dass diese Optimierung ein wichtiger Teil von Figmas Erfolg ist.
Interessante Details dazu, wie Figma zur Lösung von Sicherheitsproblemen, also Berechtigungen, eine eigene TypeScript-DSL + Compiler gebaut hat, gibt es auch in einem anderen Blogbeitrag.
https://www.figma.com/blog/how-we-rolled-out-our-own-permiss...
Das tut ein bisschen weh. Jedes große Unternehmen hat eigene interne Tools, Sprachen, Dinge wie Kubernetes. Warum teilen sie sie nicht?
Wenn Skew Open Source gewesen wäre, hätte daraus vielleicht ein besseres TypeScript werden können.
Nur weil etwas Open Source ist, entstehen nicht automatisch kostenlose Beiträge. Jeder nichttriviale PR bringt lange Reviews, Diskussionen und vielleicht sogar ein Neuschreiben mit sich.
Ich mag TypeScript und habe auch ein Full-Stack-System in TypeScript, aber perfekt ist es nicht. TypeScript in einem Monorepo einzurichten, ist ein Albtraum.
Damit es zusammen mit internen Packages unter einem pnpm-Monorepo nachvollziehbar funktioniert, muss man sehr viel an
tsconfig.jsonvon Hand anpassen, damit alle Pfade zueinander passen.Und bis das hervorragende Package
tsxaufkam, war die Produktions-Toolchain praktisch nicht wartbar.Außerdem ist es absurd langsam. Wegen Zod gab es ein Problem, bei dem die Performance des TypeScript Language Server stark einbrach, sodass wir am Ende Project References eingeführt und Project-Reference-Redirects deaktiviert haben.
Insgesamt gibt es viel Spielraum, TypeScript besser zu machen – besonders bei Monorepos und Performance.
Das Monorepo-Problem klingt so, als würdet ihr nicht für alle Packages dieselbe Konfiguration verwenden. Wenn das so ist, würde ich zuerst erzwingen, dass überall dieselbe Konfiguration und dieselben Coding-Standards gelten. Auch das ist kein TypeScript-Problem.
Habt ihr vor
tsxvielleichtts-nodeverwendet? Falls ja:tsxist deutlich robuster. Es funktioniert einfach.Wir betreiben ein mittelgroßes Monorepo mit mehreren Apps und vielen Services größtenteils in TypeScript, und selbst mit einem langweiligen Setup auf Basis von npm workspaces funktioniert es ordentlich.
Das heißt keineswegs, dass TypeScript schnell wäre, aber da es in JavaScript geschrieben ist, ist es ohnehin schwer, schnell zu sein.
Allerdings wirkt es bei großen Anwendungen wie ein selbst geschaffenes Problem, TypeScript für die Typinferenz Code ausführen zu lassen.