JavaScript

Migração de JavaScript para TypeScript em Projetos Existentes: Do Básico ao Avançado

8 min de leitura

Migração de JavaScript para TypeScript em Projetos Existentes: Do Básico ao Avançado

Preparação e Diagnóstico do Projeto 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 para visualizar as dependências e identifique se alguma já possui tipos TypeScript nativos ou DefinitelyTyped. A configuração inicial requer a instalação do TypeScript e ferramentas complementares. Execute e inicialize um arquivo com . Comece com uma configuração conservadora: defina temporariamente e aumente gradualmente a rigidez das verificações. Isso reduz erros imediatos e permite uma transição suave. Estratégia de Migração Incremental 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 para , corrija os erros de tipo encontrados e teste a funcionalidade. Isso minimiza riscos e facilita a identificação de problemas. Configure seu bundler (Webpack, Vite ou similar) para aceitar ambos os tipos

<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>&quot;strict&quot;: 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">{

&quot;compilerOptions&quot;: {

&quot;target&quot;: &quot;ES2020&quot;,

&quot;module&quot;: &quot;commonjs&quot;,

&quot;lib&quot;: [&quot;ES2020&quot;],

&quot;outDir&quot;: &quot;./dist&quot;,

&quot;rootDir&quot;: &quot;./src&quot;,

&quot;strict&quot;: false,

&quot;esModuleInterop&quot;: true,

&quot;skipLibCheck&quot;: true,

&quot;forceConsistentCasingInFileNames&quot;: true,

&quot;resolveJsonModule&quot;: true,

&quot;declaration&quot;: true,

&quot;declarationMap&quot;: true,

&quot;sourceMap&quot;: true

},

&quot;include&quot;: [&quot;src/*/&quot;],

&quot;exclude&quot;: [&quot;node_modules&quot;, &quot;dist&quot;]

}</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: &#039;ts-loader&#039;,

exclude: /node_modules/

},

{

test: /\.jsx?$/,

use: &#039;babel-loader&#039;,

exclude: /node_modules/

}

]

},

resolve: {

extensions: [&#039;.ts&#039;, &#039;.tsx&#039;, &#039;.js&#039;, &#039;.jsx&#039;]

}

};</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(&#039;pt-BR&#039;).format(date);

}

export function calculateTotal(items) {

return items.reduce((sum, item) =&gt; 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(&#039;pt-BR&#039;).format(date);

}

interface Item {

price: number;

[key: string]: any;

}

export function calculateTotal(items: Item[]): number {

return items.reduce((sum, item) =&gt; 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 &#039;meu-pacote-sem-tipos&#039; {

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 &#039;express&#039;;

interface Usuario {

id: number;

nome: string;

email: string;

}

const app = express();

app.use(express.json());

const usuarios: Usuario[] = [];

app.post(&#039;/usuarios&#039;, (req: Request, res: Response): void =&gt; {

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(&#039;/usuarios/:id&#039;, (req: Request, res: Response): void =&gt; {

const usuario = usuarios.find(u =&gt; u.id === parseInt(req.params.id));

if (!usuario) {

res.status(404).json({ erro: &#039;Usuário não encontrado&#039; });

return;

}

res.json(usuario);

});

app.listen(3000, () =&gt; console.log(&#039;Servidor rodando na porta 3000&#039;));</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">{

&quot;compilerOptions&quot;: {

&quot;strict&quot;: true,

&quot;noImplicitAny&quot;: true,

&quot;strictNullChecks&quot;: true,

&quot;strictFunctionTypes&quot;: true,

&quot;noUnusedLocals&quot;: true,

&quot;noUnusedParameters&quot;: true,

&quot;noImplicitReturns&quot;: 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) =&gt; {

return dados.map(item =&gt; item.valor * 2);

};

// Depois (rigoroso)

interface Dado {

valor: number;

}

const processarDados = (dados: Dado[]): number[] =&gt; {

return dados.map(item =&gt; item.valor * 2);

};

// Com tratamento de erros

const processarDadosSeguro = (dados: unknown[]): number[] =&gt; {

if (!Array.isArray(dados)) {

throw new Error(&#039;Dados deve ser um array&#039;);

}

return dados

.filter((item): item is Dado =&gt; typeof item === &#039;object&#039; &amp;&amp; item !== null &amp;&amp; &#039;valor&#039; in item)

.map(item =&gt; 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 &#039;../src/utils&#039;;

describe(&#039;Utils&#039;, () =&gt; {

test(&#039;formatDate deve retornar string formatada&#039;, () =&gt; {

const data = new Date(&#039;2024-01-15&#039;);

expect(formatDate(data)).toMatch(/15/);

});

test(&#039;calculateTotal deve somar preços&#039;, () =&gt; {

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>

Comentários

Mais em JavaScript

Formulários em JavaScript: Validação, Submissão e FormData: Do Básico ao Avançado
Formulários em JavaScript: Validação, Submissão e FormData: Do Básico ao Avançado

Validação de Formulários com JavaScript A validação de formulários é a primei...

Event Loop em JavaScript: Call Stack, Task Queue e Microtasks na Prática
Event Loop em JavaScript: Call Stack, Task Queue e Microtasks na Prática

Call Stack: O Coração da Execução O Call Stack é uma estrutura de dados que r...

Boas Práticas de Proxy e Reflect em JavaScript: Interceptando Operações em Objetos para Times Ágeis
Boas Práticas de Proxy e Reflect em JavaScript: Interceptando Operações em Objetos para Times Ágeis

Introdução: Por que Proxy e Reflect Importam para Times Ágeis Proxy e Reflect...