Pular para o conteúdo
TypeScript

Migrar de JavaScript para TypeScript 2026

Migrar um projeto JavaScript inteiro pra TypeScript de uma vez é receita pra desastre. A abordagem certa é incremental: ative allowJs, renomeie arquivo por arquivo, adicione tipos aos

Por que isso é importante

Migrar de JavaScript para TypeScript 2026. Migrar um projeto JavaScript inteiro pra TypeScript de uma vez é receita pra desastre. A abordagem certa é incremental: ative allowJs, renomeie arquivo por arquivo, adicione tipos aos poucos e ligue o strict só quando tudo estiver coberto. Veja como fazer sem quebrar nada.

Estratégia de Migração Incremental

A chave pra uma migração bem-sucedida é nunca quebrar o que já funciona. O TypeScript foi projetado pra coexistir com JavaScript. Com allowJs ativado, arquivos .ts e .js vivem no mesmo projeto sem conflito. Você migra um arquivo de cada vez.

A ordem importa. Comece pelos arquivos mais simples e pelos módulos que outros importam (utils, helpers, tipos compartilhados). Quando as fundações estão tipadas, os arquivos que dependem delas ganham autocomplete e checagem quase de graça.

Strict mode vem por último. Primeiro faça tudo compilar sem strict. Depois ative strictNullChecks, depois noImplicitAny, e por fim strict completo. Cada etapa revela uma camada de problemas que você resolve isoladamente.

Não tente ser perfeccionista. Na fase inicial, usar some any é aceitável. O objetivo é tirar o projeto do JavaScript puro. Você refina a tipagem com o tempo. Progresso gradual sempre ganha de perfeição paralisante.

Passo a Passo: Do JS ao TS

Siga essa sequência e seu projeto migra sem interrupção. Cada passo é um marco concreto.

  1. Passo 1 - Instale o TypeScript: Rode npm install --save-dev typescript @types/node. Se usa React, adicione @types/react @types/react-dom. Se usa Express, @types/express. Instale os @types de tudo que você importa.
  2. Passo 2 - Crie o tsconfig.json com allowJs: Rode npx tsc --init. Ative allowJs: true e checkJs: false por enquanto. Coloque strict: false. Defina outDir e rootDir. Isso faz o TypeScript aceitar arquivos .js sem reclamar.
  3. Passo 3 - Renomeie o arquivo mais simples pra .ts: Escolha um util simples, tipo formatDate.js, e renomeie pra formatDate.ts. Corrija os poucos erros que aparecem. Garanta que tudo compila e roda. Commite.
  4. Passo 4 - Crie um arquivo de tipos compartilhados: Crie src/types/index.ts com as interfaces que o projeto usa: User, Product, Config, etc. À medida que migra arquivos, importe esses tipos. Centralizar tipos evita duplicação.
  5. Passo 5 - Migre arquivos de baixo pra cima: Utils primeiro, depois services, depois controllers/componentes. Quando um módulo base tá tipado, todos que importam dele ganham checagem automática.
  6. Passo 6 - Ative strict quando tudo for .ts: Quando o último .js virar .ts, ative strict: true no tsconfig. Resolva os erros que aparecem. Esse é o momento de eliminar os any que sobraram e refinar tipos.

Configuração Inicial do tsconfig para Migração

O tsconfig de migração é diferente do tsconfig final. Ele precisa ser permissivo no começo e ir apertando conforme o projeto avança.

tsconfig.json - Fase 1 (Início da Migração)

// tsconfig.json - FASE 1: aceitar JS e TS juntos
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./src",

    // MIGRAÇÃO: aceitar JavaScript
    "allowJs": true,       // aceita .js junto com .ts
    "checkJs": false,      // não checa erros em .js (por enquanto)

    // PERMISSIVO: sem strict no começo
    "strict": false,
    "noImplicitAny": false,

    // COMPATIBILIDADE
    "esModuleInterop": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

tsconfig.json - Fase 2 (Meio da Migração)

// tsconfig.json - FASE 2: começar a apertar
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./src",

    "allowJs": true,
    "checkJs": true,       // agora checa .js também!

    // APERTANDO: ativa alguns checks
    "strict": false,
    "strictNullChecks": true,   // pega null/undefined
    "noImplicitAny": false,     // ainda aceita any implícito

    "esModuleInterop": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

tsconfig.json - Fase 3 (Migração Completa)

// tsconfig.json - FASE 3: tudo tipado, strict ativado
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2022"],
    "outDir": "./dist",
    "rootDir": "./src",

    // MIGRAÇÃO COMPLETA: desativa allowJs
    "allowJs": false,
    // "checkJs": não precisa mais

    // STRICT TOTAL
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitReturns": true,

    "esModuleInterop": true,
    "resolveJsonModule": true,
    "forceConsistentCasingInFileNames": true,
    "declaration": true,
    "sourceMap": true,
    "skipLibCheck": true,

    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

Percebe a evolução? A fase 1 só quer que o projeto compile. A fase 2 começa a apertar com checkJs e strictNullChecks. A fase 3 é o destino final: tudo em TypeScript, strict completo, sem compromissos.

Técnicas Práticas de Migração

Além do tsconfig, tem técnicas específicas que aceleram a migração e reduzem a chance de quebrar coisas.

Renomeando Arquivos com Segurança

# Renomear um arquivo por vez
mv src/utils/formatDate.js src/utils/formatDate.ts

# Verificar se compila
npx tsc --noEmit

# Se tiver erros, corrigir antes de continuar
# Commitar após cada arquivo migrado
git add .
git commit -m "migrate: formatDate.js -> formatDate.ts"

# Dica: use git mv pra manter o histórico
git mv src/utils/helpers.js src/utils/helpers.ts

Adicionando Tipos Gradualmente

// ANTES: JavaScript puro
function createUser(name, email, age) {
  return {
    id: Math.random().toString(36),
    name,
    email,
    age,
    createdAt: new Date(),
  };
}

// DEPOIS - Fase 1: Tipos mínimos (aceita any temporário)
function createUser(name: string, email: string, age: number) {
  return {
    id: Math.random().toString(36),
    name,
    email,
    age,
    createdAt: new Date(),
  };
}

// DEPOIS - Fase 2: Interface completa
interface User {
  id: string;
  name: string;
  email: string;
  age: number;
  createdAt: Date;
}

function createUser(name: string, email: string, age: number): User {
  return {
    id: Math.random().toString(36),
    name,
    email,
    age,
    createdAt: new Date(),
  };
}

Lidando com Bibliotecas sem Tipos

// 1. Primeiro, tente instalar @types
npm install --save-dev @types/lodash
npm install --save-dev @types/express

// 2. Se não existir @types, crie declaração local
// Crie src/types/minha-lib.d.ts
declare module "minha-lib-sem-tipos" {
  export function doSomething(input: string): number;
  export interface Config {
    timeout: number;
    retries: number;
  }
}

// 3. Se a lib é complexa demais, use declaração mínima
// Crie src/types/lib-complexa.d.ts
declare module "lib-complexa" {
  const lib: any;
  export default lib;
}
// ATENÇÃO: isso é temporário! Adicione tipos reais depois.

// 4. Verificar se @types existe
// https://www.npmjs.com/~types
// Ou: npx typesync (instala @types automaticamente)

Usando JSDoc como Ponte (Sem Renomear)

// Com checkJs ativado, JSDoc dá tipagem sem mudar extensão!

/**
 * @param {string} name
 * @param {string} email
 * @returns {{ id: string, name: string, email: string }}
 */
function createUser(name, email) {
  return {
    id: crypto.randomUUID(),
    name,
    email,
  };
}

// O TypeScript lê os JSDoc comments e faz checagem!
// Isso é ótimo pra projetos onde renomear arquivos é arriscado.

/**
 * @typedef {Object} DatabaseConfig
 * @property {string} host
 * @property {number} port
 * @property {string} database
 * @property {boolean} [ssl] - Opcional
 */

/** @type {DatabaseConfig} */
const dbConfig = {
  host: "localhost",
  port: 5432,
  database: "myapp",
};

Erros Comuns na Migração

Armadilhas que atrasam a migração

Tentar migrar tudo de uma vez: renomear 200 arquivos de .js pra .ts gera centenas de erros que ninguém resolve. Migre um arquivo por commit. Progresso visível, rollback fácil.

Ativar strict desde o início: strict com allowJs é uma combinação que gera erros em todos os arquivos JavaScript. Comece com strict: false e ative incrementalmente: primeiro strictNullChecks, depois noImplicitAny, por último strict completo.

Ignorar @types de dependências: se você usa Express, Lodash ou qualquer lib sem tipos nativos, instale os @types correspondentes. Sem eles, tudo que vem dessas libs é any.

Não criar um arquivo de tipos centralizados: sem um src/types/index.ts, cada dev cria tipos soltos em arquivos aleatórios. Centralizar tipos facilita reuso e evita duplicação.

Não atualizar o CI/CD: seu pipeline precisa rodar tsc --noEmit pra checar tipos. Se o CI só roda testes, erros de tipo passam despercebidos. Adicione checagem de tipos no pipeline junto com os testes.

Checklist de Migração JS para TS

  • TypeScript e @types das dependências instalados
  • tsconfig.json criado com allowJs: true e strict: false
  • Primeiro arquivo .js renomeado pra .ts e compilando
  • Arquivo src/types/index.ts criado com interfaces compartilhadas
  • Utils e helpers migrados primeiro (dependências de base)
  • Services e controllers migrados depois
  • checkJs ativado pra checar arquivos .js restantes
  • strictNullChecks ativado e erros resolvidos
  • noImplicitAny ativado e erros resolvidos
  • strict: true ativado com zero erros de compilação
  • allowJs removido (todos os arquivos são .ts)
  • Pipeline de CI rodando tsc --noEmit

Migre com Confiança pro TypeScript

Migrar de JavaScript pra TypeScript é uma das decisões mais impactantes que você faz num projeto. No CrazyStack, todo o projeto é construído em TypeScript desde o primeiro arquivo. Você aprende não só a linguagem, mas os padrões de arquitetura que fazem TypeScript brilhar em projetos de produção.

Se você tem um projeto JavaScript e quer dar o próximo passo, a migração incremental é o caminho mais seguro. E quando estiver pronto pra construir algo do zero em TypeScript, o CrazyStack te leva do setup ao deploy.