<h2>Preparação e Diagnóstico do Projeto</h2>
<p>Antes de iniciar qualquer migração, você precisa entender o estado atual do seu projeto. A primeira etapa é criar um inventário: quantos arquivos JavaScript existem, quais dependências estão instaladas e qual é a complexidade do código. Execute <code>npm list</code> para visualizar as dependências e identifique se alguma já possui tipos TypeScript nativos ou DefinitelyTyped.</p>
<p>A configuração inicial requer a instalação do TypeScript e ferramentas complementares. Execute <code>npm install --save-dev typescript ts-node</code> e inicialize um arquivo <code>tsconfig.json</code> com <code>npx tsc --init</code>. Comece com uma configuração conservadora: defina <code>"strict": false</code> temporariamente e aumente gradualmente a rigidez das verificações. Isso reduz erros imediatos e permite uma transição suave.</p>
<pre><code class="language-json">{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"strict": false,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"declaration": true,
"declarationMap": true,
"sourceMap": true
},
"include": ["src/*/"],
"exclude": ["node_modules", "dist"]
}</code></pre>
<h2>Estratégia de Migração Incremental</h2>
<p>A melhor abordagem é migrar arquivos gradualmente, começando pelos menos dependentes (utilitários, helpers) e evoluindo para módulos centrais. Renomeie apenas um arquivo por vez de <code>.js</code> para <code>.ts</code>, corrija os erros de tipo encontrados e teste a funcionalidade. Isso minimiza riscos e facilita a identificação de problemas.</p>
<p>Configure seu bundler (Webpack, Vite ou similar) para aceitar ambos os tipos de arquivo durante a transição. No <code>webpack.config.js</code>, garanta que TypeScript seja processado corretamente:</p>
<pre><code class="language-javascript">module.exports = {
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.jsx?$/,
use: 'babel-loader',
exclude: /node_modules/
}
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
}
};</code></pre>
<p>Exemplo prático: migre um módulo de utilitários primeiro. Considere este arquivo JavaScript:</p>
<pre><code class="language-javascript">// utils.js (antes)
export function formatDate(date) {
return new Intl.DateTimeFormat('pt-BR').format(date);
}
export function calculateTotal(items) {
return items.reduce((sum, item) => sum + item.price, 0);
}</code></pre>
<p>Convertendo para TypeScript:</p>
<pre><code class="language-typescript">// utils.ts (depois)
export function formatDate(date: Date): string {
return new Intl.DateTimeFormat('pt-BR').format(date);
}
interface Item {
price: number;
[key: string]: any;
}
export function calculateTotal(items: Item[]): number {
return items.reduce((sum, item) => sum + item.price, 0);
}</code></pre>
<h2>Tipagem de Módulos e Dependências</h2>
<p>Um desafio comum é lidar com dependências que não possuem tipos nativos. Para pacotes populares, procure no DefinitelyTyped (<code>@types/nome-do-pacote</code>). Instale <code>npm install --save-dev @types/express @types/node</code> conforme necessário.</p>
<p>Para dependências sem tipos, crie um arquivo de declaração. Na raiz, crie uma pasta <code>types/</code> com arquivos <code>.d.ts</code>:</p>
<pre><code class="language-typescript">// types/meu-pacote.d.ts
declare module 'meu-pacote-sem-tipos' {
export function minhaFuncao(param: string): void;
export interface MinhaInterface {
propriedade: string;
}
}</code></pre>
<p>Um exemplo prático com Express (aplicação real):</p>
<pre><code class="language-typescript">// src/server.ts
import express, { Request, Response } from 'express';
interface Usuario {
id: number;
nome: string;
email: string;
}
const app = express();
app.use(express.json());
const usuarios: Usuario[] = [];
app.post('/usuarios', (req: Request, res: Response): void => {
const novoUsuario: Usuario = {
id: usuarios.length + 1,
nome: req.body.nome,
email: req.body.email
};
usuarios.push(novoUsuario);
res.status(201).json(novoUsuario);
});
app.get('/usuarios/:id', (req: Request, res: Response): void => {
const usuario = usuarios.find(u => u.id === parseInt(req.params.id));
if (!usuario) {
res.status(404).json({ erro: 'Usuário não encontrado' });
return;
}
res.json(usuario);
});
app.listen(3000, () => console.log('Servidor rodando na porta 3000'));</code></pre>
<h2>Refinamento Progressivo de Tipos</h2>
<p>Após migrar o essencial, aumente gradualmente o nível de rigor do TypeScript. Modifique o <code>tsconfig.json</code> incrementalmente:</p>
<pre><code class="language-json">{
"compilerOptions": {
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true
}
}</code></pre>
<p>Essa abordagem força a tipagem explícita de variáveis, retornos de funções e tratamento de <code>null</code>/<code>undefined</code>. Exemplo de refatoração:</p>
<pre><code class="language-typescript">// Antes (permissivo)
const processarDados = (dados) => {
return dados.map(item => item.valor * 2);
};
// Depois (rigoroso)
interface Dado {
valor: number;
}
const processarDados = (dados: Dado[]): number[] => {
return dados.map(item => item.valor * 2);
};
// Com tratamento de erros
const processarDadosSeguro = (dados: unknown[]): number[] => {
if (!Array.isArray(dados)) {
throw new Error('Dados deve ser um array');
}
return dados
.filter((item): item is Dado => typeof item === 'object' && item !== null && 'valor' in item)
.map(item => item.valor * 2);
};</code></pre>
<p>Configure testes para garantir que a migração não quebrou funcionalidades. Use Jest com suporte TypeScript:</p>
<pre><code class="language-typescript">// __tests__/utils.test.ts
import { formatDate, calculateTotal } from '../src/utils';
describe('Utils', () => {
test('formatDate deve retornar string formatada', () => {
const data = new Date('2024-01-15');
expect(formatDate(data)).toMatch(/15/);
});
test('calculateTotal deve somar preços', () => {
const items = [{ price: 10 }, { price: 20 }];
expect(calculateTotal(items)).toBe(30);
});
});</code></pre>
<h2>Conclusão</h2>
<p>Migrar para TypeScript é uma decisão estratégica que aumenta a confiabilidade do código. <strong>Primeiro aprendizado</strong>: comece devagar e de forma incremental — renomeie poucos arquivos por vez e mantenha ambos os tipos durante a transição. <strong>Segundo aprendizado</strong>: configure o <code>tsconfig.json</code> de forma progressiva, iniciando com <code>strict: false</code> e aumentando a rigidez conforme se adapta ao sistema de tipos. <strong>Terceiro aprendizado</strong>: invista em testes automatizados para validar que nada quebrou durante o processo. Com planejamento e disciplina, sua base de código se tornará mais segura e mantível.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.typescriptlang.org/docs/" target="_blank" rel="noopener noreferrer">Documentação Oficial TypeScript</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Migration Guide</a></li>
<li><a href="https://github.com/DefinitelyTyped/DefinitelyTyped" target="_blank" rel="noopener noreferrer">DefinitelyTyped - Repository de Tipos</a></li>
<li><a href="https://expressjs.com/en/resources/middleware/cors.html" target="_blank" rel="noopener noreferrer">Express TypeScript Setup</a></li>
<li><a href="https://jestjs.io/docs/getting-started#using-typescript" target="_blank" rel="noopener noreferrer">Jest TypeScript Configuration</a></li>
</ul>