12k
All articles

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.

OpenReplay Team
OpenReplay Team
TypeScript no Node: A Configuração Prática

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 tsc para 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 type para imports apenas de tipos para evitar erros de runtime com remoção de tipos
  • Execute tsc --noEmit no 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: NodeNext e moduleResolution: NodeNext: Corresponde ao comportamento real de resolução de módulos do Node
  • verbatimModuleSyntax: Requer import type explí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 for utils.ts
  • Respeita regras ESM vs CJS: O campo type do seu package.json importa
  • 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 do package.json

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.

Open-source session replay

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

We use cookies to improve your experience. By using our site, you accept cookies.