TypeScript no Node: A Configuração Prática
Configurar TypeScript no Node.js com ESM, compilação via tsc e type-stripping nativo permite construir APIs e scripts de produção com ferramentas modernas.
Você já escreve TypeScript para o navegador. Agora você precisa executá-lo no lado do servidor—para uma API, um script de build ou SSR. O problema: a maioria dos guias de configuração está desatualizada, recomendando configurações CommonJS ou ferramentas que não se alinham com o Node.js moderno.
Este guia cobre duas abordagens para sua configuração TypeScript Node.js: compilar com tsc e executar JavaScript, ou executar arquivos .ts diretamente com a remoção nativa de tipos do Node. Ambas funcionam. Cada uma se adequa a cenários diferentes.
Principais Conclusões
- Defina
"type": "module"no package.json para habilitar ESM por padrão em projetos TypeScript Node.js modernos - Use compilação
tscpara deploys de produção, pacotes publicados e código que usa enums, namespaces ou propriedades de parâmetros - Use a remoção nativa de tipos do Node para scripts locais, servidores de desenvolvimento e protótipos rápidos
- Sempre use
import typepara imports apenas de tipos para evitar erros de runtime com remoção de tipos - Execute
tsc --noEmitno CI, pois a remoção de tipos do Node não realiza verificação de tipos
A Base: Node 24 LTS e ESM
Comece com esta fundação:
{
"type": "module"
}
Isso habilita ESM por padrão. Seus imports usam sintaxe ESM, e o Node resolve módulos de acordo.
Node 24 é a baseline LTS atual para esta configuração (pode ser baixado aqui: https://nodejs.org/en/download).
Abordagem 1: Compilar com tsc, Executar JavaScript
Esta abordagem separa compilação de execução. Use-a para deploys de produção, pacotes publicados ou quando você precisar de suporte completo aos recursos do TypeScript.
tsconfig para Node 24
{
"compilerOptions": {
"target": "ES2024",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": "src",
"outDir": "dist",
"strict": true,
"skipLibCheck": true,
"declaration": true,
"sourceMap": true,
"verbatimModuleSyntax": true,
"isolatedModules": true,
"lib": ["ES2024"]
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}
Configurações-chave nesta configuração:
module: NodeNextemoduleResolution: NodeNext: Corresponde ao comportamento real de resolução de módulos do NodeverbatimModuleSyntax: Requerimport typeexplícito para imports apenas de tipos—crítico para evitar erros de runtime (veja a documentação do TypeScript: https://www.typescriptlang.org/tsconfig#verbatimModuleSyntax)isolatedModules: Garante compatibilidade com ferramentas de transpilação de arquivo único
Scripts
{
"scripts": {
"build": "tsc",
"start": "node dist/index.js",
"dev": "tsc --watch"
}
}
Execute npm run build, depois npm start. O JavaScript compilado fica em dist/.
Abordagem 2: TypeScript Nativo do Node (Remoção de Tipos)
O Node introduziu suporte nativo ao TypeScript no Node 22 e o estabilizou no Node 24 LTS via remoção de tipos. Use-o para scripts, ferramentas locais ou desenvolvimento quando você quiser zero etapas de build.
Documentação oficial: https://nodejs.org/api/typescript.html
Como Funciona
No Node 24+, execute diretamente:
node src/index.ts
(Versões mais antigas do Node requeriam flags experimentais; Node 24 não requer.)
Limitações Críticas
O TypeScript nativo do Node apenas remove tipos—ele não faz verificação de tipos. Você ainda precisa de tsc --noEmit no CI ou no seu editor para capturar erros.
Outras restrições:
- Ignora tsconfig.json: O Node não lê suas opções de compilador
- Requer extensões de arquivo explícitas: Escreva
import { foo } from './utils.js'mesmo quando o arquivo fonte forutils.ts - Respeita regras ESM vs CJS: O campo type do seu
package.jsonimporta - Não executará TypeScript de node_modules: Dependências devem ser JavaScript compilado
- Suporta apenas sintaxe apagável: Enums, namespaces e propriedades de parâmetros falham a menos que você habilite
--experimental-transform-types
Extensões de Arquivo Importam
Para formatos de módulo mistos:
- Arquivos
.mts→ sempre ESM - Arquivos
.cts→ sempre CommonJS - Arquivos
.ts→ seguem o campo type dopackage.json
Discover how at OpenReplay.com.
Evitando Erros de Runtime
Use import type para imports apenas de tipos:
// Correto
import type { Request, Response } from 'express'
import express from 'express'
// Errado - falhará em runtime com remoção de tipos
import { Request, Response } from 'express'
Habilite verbatimModuleSyntax no seu tsconfig para capturar esses casos durante o desenvolvimento, mesmo que o Node ignore a configuração em runtime.
Qual Abordagem Usar
Use compilação tsc para:
- Deploys de produção
- Pacotes npm publicados
- Código usando enums, namespaces ou propriedades de parâmetros
- Projetos que requerem source maps em produção
Use remoção nativa de tipos para:
- Scripts e ferramentas locais
- Servidores de desenvolvimento (combine com
--watch) - Protótipos rápidos
- Builds de desenvolvimento SSR
Uma Configuração Prática de Desenvolvimento
Combine ambas as abordagens:
{
"scripts": {
"dev": "node --watch src/index.ts",
"typecheck": "tsc --noEmit",
"build": "tsc",
"start": "node dist/index.js"
}
}
O desenvolvimento usa execução nativa para velocidade. O CI executa typecheck. A produção faz deploy de JavaScript compilado.
Conclusão
A configuração moderna de TypeScript Node.js é mais simples do que guias antigos sugerem. Use ESM, configure resolução de módulo NodeNext e escolha sua estratégia de execução baseada no contexto. A remoção nativa de tipos funciona para desenvolvimento e scripts. A saída compilada funciona para produção e pacotes. Ambas as abordagens compartilham o mesmo código fonte e tsconfig—você não está preso a nenhuma delas.
Perguntas Frequentes
Por que preciso usar extensões .js nos imports quando meus arquivos fonte são .ts?
A resolução de módulos do Node requer extensões de arquivo explícitas para ESM. Quando você escreve import from ./utils.js, o Node procura por esse caminho exato em runtime, mesmo que seu arquivo fonte seja utils.ts. Como a remoção de tipos remove tipos mas não renomeia arquivos, e o tsc gera arquivos .js, usar extensões .js no seu código fonte garante que os imports funcionem em ambos os cenários.
Posso usar enums com a remoção nativa de tipos do Node?
Não por padrão. Enums requerem transformação de código, não apenas remoção de tipos. Você pode habilitar a flag --experimental-transform-types para suportar enums, namespaces e propriedades de parâmetros, mas isso adiciona complexidade. Para configurações mais simples, considere usar objetos const com asserções as const como alternativa aos enums.
Ainda preciso de ts-node ou tsx com Node 24?
Para a maioria dos casos de uso, não. A remoção nativa de tipos do Node 24 lida com a execução direta de .ts. Ferramentas como ts-node e tsx são conveniências opcionais que adicionam suporte ao tsconfig.json, resolução de alias de caminho e transformações completas do TypeScript sem flags. Use-as apenas se sua configuração precisar desses recursos.
Como faço debug de TypeScript no Node sem source maps compilados?
Ao usar remoção nativa de tipos, o Node executa seus arquivos .ts diretamente, então os números de linha nos stack traces correspondem ao seu código fonte. Para código compilado, habilite sourceMap no tsconfig.json e o Node usará automaticamente os arquivos .js.map para mostrar as localizações originais do TypeScript em erros e sessões de debug.
Complete picture for complete understanding
Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.
Star on GitHub12k