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.
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
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.