Drei Dinge, die Leute “TypeScript kompilieren” nennen
Wenn Entwickler danach suchen, wie man TypeScript zu einer Binary kompiliert, stoßen sie meist auf drei sehr unterschiedliche Techniken, die sich ein Wort teilen:
- Transpilieren.
tsc, SWC und esbuild verwandeln TypeScript in JavaScript. Die Ausgabe braucht weiterhin Node.js, Bun oder einen Browser, um zu laufen. Es ist keine Binary im Spiel. - Eingebettete Laufzeitumgebung.
bun build --compile,deno compileund Node.js Single Executable Applications (SEA) hängen dein gebündeltes JavaScript an eine vollständige Kopie der Laufzeitumgebung an. Du bekommst eine einzelne Datei, aber die Engine reist darin mit, und dein Code wird bei jedem Prozessstart erneut geparst und JIT-kompiliert. - Ahead-of-Time-native Kompilierung. Das macht Perry. TypeScript wird mit SWC geparst, Typen werden aufgelöst, Generics werden monomorphisiert, und LLVM erzeugt Maschinencode. Der Linker erzeugt ein normales Executable — dieselbe Art von Artefakt, die eine Rust-, Go- oder C++-Toolchain erzeugt. Es steckt überhaupt keine JavaScript-Engine in der Binary.
Weil keine Engine gestartet werden muss und beim Start nichts geparst werden muss, startet eine Perry-Binary in etwa einer Millisekunde. Die Pipeline selbst wird ausführlicher auf der Seite nativer TypeScript-Compiler und in den Compiler-Interna beschrieben.
Wie groß ist die Binary?
Die Größe hängt davon ab, was eingebunden wird, denn nur der Code, den du tatsächlich benutzt, wird kompiliert und gelinkt:
- Ein Hello World liegt bei rund 330 KB.
- Typische CLI-Tools landen bei 2–5 MB.
- Vollständige Anwendungen, die große Frameworks einbinden (Fastify, mysql2 und Ähnliches), liegen bei rund 48 MB.
Zum Vergleich: Ein Node-SEA-Executable ist eine Kopie der node-Binary selbst, es startet also je nach Plattform bei etwa 88–118 MB, bevor dein Code hinzukommt, und ein mit Bun kompiliertes Hello World misst rund 60 MB auf macOS arm64 und rund 100 MB auf Linux x64, weil die komplette Bun-Laufzeitumgebung eingebettet ist.
Perry vs bun build --compile vs Node SEA
Alle drei liefern dir eine einzelne Datei, die du weitergeben kannst. Ansonsten sind es sehr unterschiedliche Tools, und jedes ist für jemanden die richtige Antwort:
| Perry | bun build --compile | Node SEA | |
|---|---|---|---|
| Was es erzeugt | AOT-kompilierter Maschinencode (LLVM) | Gebündeltes JS + eingebettete Bun-Laufzeitumgebung | Kopie der node-Binary mit eingeschleustem gebündeltem Skript |
| Ausführungsmodell | Nativer Code, keine JS-Engine | JIT (JavaScriptCore) zur Laufzeit | JIT (V8) zur Laufzeit |
| Hello-World-Größe | ~330 KB | ~60 MB (macOS arm64) bis ~100+ MB (Linux/Windows) | ~88–118 MB (Größe der node-Binary) |
| Startzeit | ~1 ms | ~10 ms | ~30 ms |
| Cross-Compilation | 10 Ziele, einschließlich Windows/macOS/iOS von Linux aus | Ja — Linux, Windows, macOS über --target | Nein — stattdessen eine plattformspezifische node-Binary kopieren |
| JS-/npm-Kompatibilität | Wachsend: axios, zod v4, express, fastify, hono kompilieren nativ; optionaler V8-Fallback für den Rest | Vollständig — es ist die Bun-Laufzeitumgebung | Vollständige Node-Semantik; erfordert vorheriges Bundling, nur CommonJS auf Node 24 LTS |
| Status | Pre-1.0 | Stabil | Stabilitätsstufe “Active development” in Node 24 LTS |
Die ehrliche Einordnung: Wenn sich deine Anwendung auf das volle npm-Ökosystem stützt und du null Kompatibilitätsrisiko willst, liefern Bun und Node SEA genau die Engine-Semantik, gegen die du ohnehin schon entwickelst — das ist ihre Stärke, und die Kosten in der Größe spielen für dein Deployment vielleicht keine Rolle. Perry ist ein anderer Trade-off. Du bekommst echte Ahead-of-Time-Kompilierung, kleine Binaries und Startzeit im Millisekundenbereich; im Gegenzug nimmst du einen Pre-1.0-Compiler in Kauf, dessen JavaScript-Konformität gemessen und veröffentlicht wird (test262: String 79 %, Array 72 % Stand v0.5.1146), statt von V8 geerbt zu sein.
Ausführliche direkte Vergleiche: Perry vs Bun und Perry vs Deno. Wie npm-Pakete kompilieren, steht in Real npm packages and a conformance sweep.