Back

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

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

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.

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.

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.

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.

Check our GitHub repo and join the thousands of developers in our community.

OpenReplay