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