Tres cosas que la gente llama «compilar TypeScript»
Cuando los desarrolladores buscan cómo compilar TypeScript a un binario, normalmente se topan con tres técnicas muy distintas que comparten una palabra:
- Transpilación.
tsc, SWC y esbuild convierten TypeScript en JavaScript. La salida todavía necesita Node.js, Bun o un navegador para ejecutarse. No hay ningún binario involucrado. - Runtime embebido.
bun build --compile,deno compiley las Single Executable Applications (SEA) de Node.js concatenan tu JavaScript empaquetado con una copia completa del runtime. Obtienes un único archivo, pero el motor viaja dentro de él y tu código se sigue parseando y compilando con JIT cada vez que arranca el proceso. - Compilación nativa anticipada. Esto es lo que hace Perry. TypeScript se parsea con SWC, se resuelven los tipos, los genéricos se monomorfizan y LLVM emite código máquina. El linker produce un ejecutable normal — el mismo tipo de artefacto que produce un toolchain de Rust, Go o C++. No hay ningún motor de JavaScript en el binario.
Como no hay ningún motor que arrancar ni nada que parsear al inicio, un binario de Perry arranca en aproximadamente un milisegundo. La pipeline en sí se describe con más detalle en la página compilador nativo de TypeScript y en los internos del compilador.
¿Qué tamaño tiene el binario?
El tamaño depende de lo que incluyas, porque solo se compila y enlaza el código que realmente usas:
- Un hello world ronda los 330 KB.
- Las herramientas CLI típicas se sitúan en 2–5 MB.
- Las aplicaciones completas que enlazan frameworks grandes (Fastify, mysql2 y similares) rondan los 48 MB.
Para contrastar: un ejecutable Node SEA es una copia del binario node en sí, así que arranca en torno a 88–118 MB según la plataforma antes de añadir tu código, y un hello world compilado con Bun mide unos 60 MB en macOS arm64 y alrededor de 100 MB en Linux x64, porque se embebe el runtime completo de Bun.
Perry frente a bun build --compile frente a Node SEA
Los tres te dan un único archivo que puedes entregarle a alguien. Por lo demás son herramientas muy distintas, y cada una es la respuesta correcta para alguien:
| Perry | bun build --compile | Node SEA | |
|---|---|---|---|
| Qué produce | Código máquina compilado con AOT (LLVM) | JS empaquetado + runtime de Bun embebido | Copia del binario node con tu script empaquetado inyectado |
| Modelo de ejecución | Código nativo, sin motor JS | JIT (JavaScriptCore) en runtime | JIT (V8) en runtime |
| Tamaño del hello world | ~330 KB | ~60 MB (macOS arm64) a ~100+ MB (Linux/Windows) | ~88–118 MB (tamaño del binario node) |
| Arranque | ~1 ms | ~10 ms | ~30 ms |
| Compilación cruzada | 10 plataformas, incluyendo Windows/macOS/iOS desde Linux | Sí — Linux, Windows, macOS mediante --target | No — en su lugar, copia un binario node específico de cada plataforma |
| Compatibilidad JS/npm | Creciente: axios, zod v4, express, fastify y hono compilan de forma nativa; fallback opcional con V8 para el resto | Total — es el runtime de Bun | Semántica completa de Node; requiere empaquetado previo, solo CommonJS en Node 24 LTS |
| Estado | Pre-1.0 | Estable | Estabilidad «en desarrollo activo» en Node 24 LTS |
Con honestidad: si tu aplicación depende del ecosistema npm completo y quieres cero riesgo de compatibilidad, Bun y Node SEA ejecutan exactamente la semántica del motor contra la que ya desarrollas — esa es su fortaleza, y el coste en tamaño puede no importarte para tu despliegue. Perry es una apuesta distinta. Obtienes compilación anticipada real, binarios pequeños y arranque en milisegundos; a cambio, adoptas un compilador pre-1.0 cuya conformidad con JavaScript se mide y se publica (test262: String 79%, Array 72% a fecha de v0.5.1146) en lugar de heredarla de V8.
Comparativas detalladas: Perry frente a Bun y Perry frente a Deno. Para ver cómo compilan los paquetes npm, consulta Paquetes npm reales y un barrido de conformidad.