E se o TypeScript pudesse compilar diretamente para código nativo?
Hoje todo programa TypeScript passa pelo mesmo caminho: os tipos são apagados, o código vira JavaScript e uma VM como o V8 precisa redescobrir, em tempo de execução, informações que o programador já havia escrito.
Mas e se o compilador pudesse aproveitar essas informações para gerar código nativo?
Foi exatamente essa pergunta que motivou o desenvolvimento do RTS, um compilador TypeScript para código nativo escrito em Rust e baseado no Cranelift.
A ideia
Quando o compilador consegue provar que um valor sempre será um number, por exemplo, ele pode mantê-lo diretamente em registradores da CPU, evitando boxing, verificações de tipo e chamadas para uma máquina virtual.
Quando isso não é possível (any, operador + polimórfico, fronteiras de JSON etc.), o RTS retorna para um modelo dinâmico baseado em NaN-boxing de64 bits, com hidden classes para layout de objetos e inline caches orientados a dados que funcionam até em AOT.
Ou seja, o objetivo não é eliminar a natureza dinâmica do JavaScript, mas pagar esse custo apenas quando realmente necessário.
Como funciona
- SWC faz o parsing do TypeScript
- Geração de uma HIR tipada
- Lowering único para Cranelift IR
- O mesmo caminho alimenta a execução via JIT (rts run) e a geração de executáveis nativos standalone (rts compile)
O projeto também possui:
- GC próprio (mark-and-sweep que entende o NaN-box)
- Suporte a N-API — addons .node como o bcrypt já funcionam
- Servidor HTTP nativo com pico de ~29k req/s
- Implementação própria de um DOM headless em Rust (não é binding de jsdom: parser HTML, cascade CSS com especificidade real e layout validado contra o Chrome)
Onde estamos
A compatibilidade é medida automaticamente contra Node e Bun com 609 testes reais de semântica JavaScript — closures, protótipos, generators,Unicode, ordem de promises. O CI re-roda a comparação a cada push e o badge no README é gerado desse relatório: hoje está em 76% e nunca é ajustado na mão. Um teste só conta como passando quando a saída bate com o Node byte a byte.
O projeto também possui uma suíte interna com 1.688 testes, e toda divergência conhecida gera automaticamente uma issue com um caso mínimo reproduzível e um único critério de aceitação: bater com o Node.
Ainda faltam recursos importantes, como partes do event loop, alguns comportamentos de Symbol, Proxy e Intl.
Sabemos que o projeto é novo e ainda não chega aos pés das engines de hoje, o V8 e as runtimes construídas sobre ele carregam décadas de engenharia. Mas o projeto já existe, compila e roda código real, e a ambição de longo prazo é clara: nos tornarmos uma runtime reconhecida ao lado de Node, Deno e Bun.
Por que estou compartilhando isso?
Somos dois desenvolvedores (com assistência pesada de IA) tocando o projeto, e achei que ele poderia gerar uma boa discussão técnica aqui. Se alguém quiser contribuir, as issues de divergência são o ponto de entrada ideal: pequenas, autocontidas, com o caso de reprodução e o critério de aceitação já prontos.
Gostaria principalmente de ouvir opiniões sobre:
- Até onde vale explorar o sistema de tipos do TypeScript para gerar código nativo?
- Quais limitações vocês enxergam nessa abordagem?
- Quais problemas vocês consideram mais difíceis em um compilador desse tipo?
Se alguém tiver interesse em conhecer o código ou acompanhar o desenvolvimento, o repositório é: