1

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

  1. SWC faz o parsing do TypeScript
  2. Geração de uma HIR tipada
  3. Lowering único para Cranelift IR
  4. 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 é:

https://github.com/UrubuCode/rts

Carregando publicação patrocinada...