Tre cose che si chiamano “compilare TypeScript”
Quando gli sviluppatori cercano come compilare TypeScript in un binario, di solito si imbattono in tre tecniche molto diverse che condividono una parola:
- Transpilazione.
tsc, SWC ed esbuild trasformano TypeScript in JavaScript. L'output ha comunque bisogno di Node.js, Bun o di un browser per essere eseguito. Nessun binario è coinvolto. - Integrazione del runtime.
bun build --compile,deno compilee le Node.js Single Executable Applications (SEA) concatenano il tuo JavaScript raggruppato con una copia completa del runtime. Ottieni un unico file, ma il motore viaggia al suo interno e il tuo codice viene comunque analizzato e compilato JIT ogni volta che il processo si avvia. - Compilazione nativa ahead-of-time. È quello che fa Perry. TypeScript viene analizzato con SWC, i tipi vengono risolti, i generici vengono monomorfizzati e LLVM genera codice macchina. Il linker produce un eseguibile normale — la stessa classe di artefatto prodotta da una toolchain Rust, Go o C++. Non c'è alcun motore JavaScript nel binario.
Poiché non c'è alcun motore da avviare né nulla da analizzare all'avvio, un binario Perry parte in circa un millisecondo. La pipeline stessa è descritta più in dettaglio nella pagina compilatore nativo per TypeScript e nei meccanismi interni del compilatore.
Quanto è grande il binario?
La dimensione dipende da cosa includi, perché viene compilato e linkato solo il codice che usi effettivamente:
- Un hello world è di circa 330 KB.
- I tipici strumenti CLI si attestano su 2–5 MB.
- Le applicazioni complete che linkano framework di grandi dimensioni (Fastify, mysql2 e simili) sono di circa 48 MB.
Per contrasto: un eseguibile Node SEA è una copia del binario node stesso, quindi parte da circa 88–118 MB a seconda della piattaforma prima ancora di aggiungere il tuo codice, mentre un hello world compilato con Bun misura circa 60 MB su macOS arm64 e circa 100 MB su Linux x64, perché l'intero runtime di Bun è embedded.
Perry vs bun build --compile vs Node SEA
Tutti e tre ti danno un unico file che puoi consegnare a qualcuno. Per il resto sono strumenti molto diversi, e ognuno è la risposta giusta per qualcuno:
| Perry | bun build --compile | Node SEA | |
|---|---|---|---|
| Cosa produce | Codice macchina compilato AOT (LLVM) | JS raggruppato + runtime Bun embedded | Copia del binario node con il tuo script raggruppato iniettato |
| Modello di esecuzione | Codice nativo, nessun motore JS | JIT (JavaScriptCore) a runtime | JIT (V8) a runtime |
| Dimensione hello-world | ~330 KB | ~60 MB (macOS arm64) a oltre 100 MB (Linux/Windows) | ~88–118 MB (dimensione del binario node) |
| Avvio | ~1 ms | ~10 ms | ~30 ms |
| Cross-compilazione | 10 target, incluso Windows/macOS/iOS da Linux | Sì — Linux, Windows, macOS tramite --target | No — copia invece un binario node per piattaforma |
| Compatibilità JS/npm | In crescita: axios, zod v4, express, fastify, hono compilano nativamente; fallback V8 opzionale per il resto | Completa — è il runtime Bun | Semantica Node completa; richiede pre-bundling, solo CommonJS su Node 24 LTS |
| Stato | Pre-1.0 | Stabile | Stabilità “in sviluppo attivo” in Node 24 LTS |
Il quadro onesto: se la tua applicazione si appoggia sull'intero ecosistema npm e vuoi un rischio di compatibilità zero, Bun e Node SEA eseguono esattamente la semantica del motore contro cui già sviluppi — questo è il loro punto di forza, e il costo in termini di dimensione potrebbe non contare per il tuo deployment. Perry è un compromesso diverso. Ottieni vera compilazione ahead-of-time, binari piccoli e avvio in millisecondi; in cambio adotti un compilatore pre-1.0 la cui conformità JavaScript è misurata e pubblicata (test262: String 79%, Array 72% a partire dalla v0.5.1146) invece di essere ereditata da V8.
Confronti diretti dettagliati: Perry vs Bun e Perry vs Deno. Per come compilano i pacchetti npm, vedi Veri pacchetti npm ora compilano: axios, zod, express — e una passata di conformance.