TypeScript

O que Todo Dev Deve Saber sobre Migração de JavaScript para TypeScript: Estratégias para Projetos Reais

11 min de leitura

O que Todo Dev Deve Saber sobre Migração de JavaScript para TypeScript: Estratégias para Projetos Reais

Entendendo o Contexto: Por Que Migrar para TypeScript? JavaScript é uma linguagem dinâmica e flexível, características que a tornaram popular, mas também geradora de bugs difíceis de rastrear em projetos grandes. TypeScript resolve esse problema ao adicionar verificação de tipos estática durante o desenvolvimento, permitindo que você capture erros antes da execução. Quando você trabalha em um projeto real com múltiplos desenvolvedores, refatorações frequentes ou código legado, essa segurança se torna essencial. A migração não precisa ser um evento traumático onde você para tudo e reescreve o projeto. Projetos maduros podem fazer essa transição de forma gradual, permitindo que arquivos TypeScript coexistam com JavaScript. Essa estratégia reduz risco, mantém a produtividade e permite que o time aprenda TypeScript naturalmente. Nesta aula, você aprenderá exatamente como fazer isso em um projeto real. Fase 1: Preparação e Configuração do Ambiente Instalando TypeScript e Configurando o Projeto Começamos adicionando TypeScript ao seu projeto. Você não precisa remover nada do JavaScript existente: Isso cria

<h2>Entendendo o Contexto: Por Que Migrar para TypeScript?</h2>

<p>JavaScript é uma linguagem dinâmica e flexível, características que a tornaram popular, mas também geradora de bugs difíceis de rastrear em projetos grandes. TypeScript resolve esse problema ao adicionar verificação de tipos estática durante o desenvolvimento, permitindo que você capture erros antes da execução. Quando você trabalha em um projeto real com múltiplos desenvolvedores, refatorações frequentes ou código legado, essa segurança se torna essencial.</p>

<p>A migração não precisa ser um evento traumático onde você para tudo e reescreve o projeto. Projetos maduros podem fazer essa transição de forma gradual, permitindo que arquivos TypeScript coexistam com JavaScript. Essa estratégia reduz risco, mantém a produtividade e permite que o time aprenda TypeScript naturalmente. Nesta aula, você aprenderá exatamente como fazer isso em um projeto real.</p>

<h2>Fase 1: Preparação e Configuração do Ambiente</h2>

<h3>Instalando TypeScript e Configurando o Projeto</h3>

<p>Começamos adicionando TypeScript ao seu projeto. Você não precisa remover nada do JavaScript existente:</p>

<pre><code class="language-bash">npm install --save-dev typescript

npx tsc --init</code></pre>

<p>Isso cria um arquivo <code>tsconfig.json</code> que controla como o TypeScript compila seu código. Para um projeto em migração, configure-o de forma permissiva inicialmente:</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;moduleResolution&quot;: &quot;node&quot;

},

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

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

}</code></pre>

<p>Note que <code>&quot;strict&quot;: false</code> inicialmente. Isso permite que arquivos TypeScript e JavaScript coexistam sem erros. Conforme ganhar confiança, você aumenta gradualmente o nível de rigor.</p>

<h3>Integrando com seu Build System</h3>

<p>Se você usa Webpack, Vite ou outro bundler, precise adicionar o loader TypeScript:</p>

<pre><code class="language-bash">npm install --save-dev ts-loader</code></pre>

<p>Para Webpack, atualize seu <code>webpack.config.js</code>:</p>

<pre><code class="language-javascript">module.exports = {

module: {

rules: [

{

test: /\.tsx?$/,

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

exclude: /node_modules/,

},

],

},

resolve: {

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

},

};</code></pre>

<h2>Fase 2: Migração Gradual de Arquivos</h2>

<h3>Começando com Módulos Isolados</h3>

<p>A chave para migração bem-sucedida é começar com arquivos que têm poucas dependências. Identifique módulos utilitários ou helpers que outras partes do código usam, mas que não dependem de muitos outros arquivos.</p>

<p>Vamos converter um arquivo de utilitários. Antes, em JavaScript puro:</p>

<pre><code class="language-javascript">// utils.js

export function formatCurrency(value) {

return &#039;$&#039; + (value / 100).toFixed(2);

}

export function calculateTax(amount, taxRate) {

return amount * (taxRate / 100);

}

export function parseDate(dateString) {

return new Date(dateString);

}</code></pre>

<p>Migre para TypeScript adicionando tipos explícitos:</p>

<pre><code class="language-typescript">// utils.ts

export function formatCurrency(value: number): string {

return &#039;$&#039; + (value / 100).toFixed(2);

}

export function calculateTax(amount: number, taxRate: number): number {

return amount * (taxRate / 100);

}

export function parseDate(dateString: string): Date {

return new Date(dateString);

}</code></pre>

<p>O compilador agora garante que <code>formatCurrency</code> recebe apenas números. Se alguém chamar <code>formatCurrency(&quot;100&quot;)</code>, o TypeScript avisará durante o desenvolvimento.</p>

<h3>Importações Seguras em Código JavaScript</h3>

<p>Seu código JavaScript ainda pode importar de módulos TypeScript sem problemas:</p>

<pre><code class="language-javascript">// calculator.js

import { formatCurrency, calculateTax } from &#039;./utils&#039;;

const total = calculateTax(1000, 10);

console.log(formatCurrency(total));</code></pre>

<p>O TypeScript compila para JavaScript normal, então essa importação funciona perfeitamente.</p>

<h2>Fase 3: Tipagem Avançada e Refatoração</h2>

<h3>Definindo Tipos Complexos</h3>

<p>Conforme você migra mais arquivos, crie tipos reutilizáveis. Vamos imaginar uma aplicação de e-commerce:</p>

<pre><code class="language-typescript">// types.ts

export type OrderStatus = &#039;pending&#039; | &#039;processing&#039; | &#039;shipped&#039; | &#039;delivered&#039;;

export interface Product {

id: string;

name: string;

price: number;

quantity: number;

}

export interface Order {

id: string;

products: Product[];

status: OrderStatus;

createdAt: Date;

totalAmount: number;

}

export interface Customer {

id: string;

email: string;

name: string;

orders: Order[];

}</code></pre>

<p>Agora qualquer arquivo que trabalhe com pedidos tem segurança de tipo:</p>

<pre><code class="language-typescript">// orderService.ts

import { Order, Product, OrderStatus } from &#039;./types&#039;;

export class OrderService {

calculateTotal(products: Product[]): number {

return products.reduce((sum, p) =&gt; sum + p.price * p.quantity, 0);

}

updateStatus(order: Order, newStatus: OrderStatus): Order {

return {

...order,

status: newStatus,

};

}

filterByStatus(orders: Order[], status: OrderStatus): Order[] {

return orders.filter(o =&gt; o.status === status);

}

}</code></pre>

<p>Se um desenvolvedor tentar passar um status inválido, o TypeScript reclama imediatamente.</p>

<h3>Genéricos para Reutilização</h3>

<p>TypeScript oferece genéricos para criar funções e classes que funcionam com qualquer tipo, mas mantêm segurança:</p>

<pre><code class="language-typescript">// repository.ts

export class Repository&lt;T&gt; {

private items: T[] = [];

add(item: T): void {

this.items.push(item);

}

findById(id: string, idField: keyof T): T | undefined {

return this.items.find(item =&gt; item[idField] === id);

}

getAll(): T[] {

return [...this.items];

}

remove(id: string, idField: keyof T): void {

this.items = this.items.filter(item =&gt; item[idField] !== id);

}

}</code></pre>

<p>Reutilize essa classe para qualquer entidade:</p>

<pre><code class="language-typescript">// main.ts

import { Repository } from &#039;./repository&#039;;

import { Order, Customer } from &#039;./types&#039;;

const orderRepository = new Repository&lt;Order&gt;();

const customerRepository = new Repository&lt;Customer&gt;();

const order: Order = {

id: &#039;123&#039;,

products: [],

status: &#039;pending&#039;,

createdAt: new Date(),

totalAmount: 0,

};

orderRepository.add(order);

const found = orderRepository.findById(&#039;123&#039;, &#039;id&#039;);</code></pre>

<h2>Fase 4: Tratamento de Dependências Externas</h2>

<h3>Tipando Bibliotecas sem Tipos</h3>

<p>Nem todas as bibliotecas npm têm tipos TypeScript. Para as que não têm, você cria um arquivo de declaração <code>.d.ts</code>:</p>

<pre><code class="language-typescript">// types/legacy-lib.d.ts

declare module &#039;legacy-lib&#039; {

export function process(data: any): string;

export class Handler {

execute(input: string): Promise&lt;void&gt;;

}

}</code></pre>

<p>Agora você pode usar a biblioteca com segurança de tipo:</p>

<pre><code class="language-typescript">// worker.ts

import { process, Handler } from &#039;legacy-lib&#039;;

const result: string = process(someData);

const handler = new Handler();

await handler.execute(result);</code></pre>

<h3>Consultando Tipos de Bibliotecas Modernas</h3>

<p>Muitas bibliotecas modernas já incluem tipos. Se usar <code>axios</code>, por exemplo:</p>

<pre><code class="language-typescript">// api.ts

import axios, { AxiosResponse } from &#039;axios&#039;;

export interface ApiResponse&lt;T&gt; {

data: T;

status: number;

}

export async function fetchUser(id: string): Promise&lt;ApiResponse&lt;{ name: string; email: string }&gt;&gt; {

const response: AxiosResponse&lt;{ name: string; email: string }&gt; = await axios.get(

/api/users/${id}

);

return {

data: response.data,

status: response.status,

};

}</code></pre>

<p>TypeScript automaticamente infere e valida que o tipo retornado corresponde ao esperado.</p>

<h2>Fase 5: Aumentando o Rigor Gradualmente</h2>

<h3>Ativando Verificações Estritas</h3>

<p>Depois que a maioria do código foi migrado, ative as verificações estritas no <code>tsconfig.json</code>:</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;noImplicitThis&quot;: true,

&quot;alwaysStrict&quot;: true

}

}</code></pre>

<p>Isso obriga a tipagem explícita em todos os lugares. Inicialmente, você verá muitos erros — isso é esperado e produtivo. Resolva-os arquivo por arquivo.</p>

<h3>Tratando Valores Nulos e Indefinidos</h3>

<p>Uma vantagem crítica do <code>strictNullChecks</code> é forçar você a lidar explicitamente com valores que podem ser nulos:</p>

<pre><code class="language-typescript">// Sem strict: compila, mas pode quebrar em runtime

function getName(user) {

return user.name.toUpperCase();

}

// Com strict: força você a verificar

function getName(user: { name?: string }): string {

return user.name?.toUpperCase() ?? &#039;UNKNOWN&#039;;

}</code></pre>

<p>Essa pequena mudança previne um dos bugs mais comuns em JavaScript.</p>

<h2>Conclusão</h2>

<p>A migração para TypeScript é um investimento que se paga rapidamente em projetos reais. Você aprendeu que a transição não precisa ser tudo ou nada — começar com módulos isolados, configurar o compilador permissivamente e aumentar gradualmente o rigor é a estratégia profissional. Os três ganhos principais são: <strong>erros capturados cedo durante o desenvolvimento</strong>, <strong>refatorações mais seguras porque o compilador valida</strong> e <strong>documentação viva através dos tipos, que nunca fica desatualizada como comentários tradicionais</strong>.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://www.typescriptlang.org/docs/" target="_blank" rel="noopener noreferrer">TypeScript Official Documentation</a></li>

<li><a href="https://www.typescriptlang.org/docs/handbook/2/types-from-types.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Types</a></li>

<li><a href="https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html" target="_blank" rel="noopener noreferrer">Migration Guide - TypeScript</a></li>

<li><a href="https://effectivetypescript.com/" target="_blank" rel="noopener noreferrer">Effective TypeScript by Dan Vanderkam</a></li>

<li><a href="https://www.youtube.com/playlist?list=PL4cUxeGkcC9gUgr39Q_yD6SE-7YCKXoversimplified" target="_blank" rel="noopener noreferrer">Learn TypeScript - The Net Ninja</a></li>

</ul>

<p>&lt;!-- FIM --&gt;</p>

Comentários

Mais em TypeScript

Decorators em TypeScript: Class, Method, Property e Parameter na Prática
Decorators em TypeScript: Class, Method, Property e Parameter na Prática

O que são Decorators em TypeScript Decorators são uma feature experimental do...

O que Todo Dev Deve Saber sobre Node.js com TypeScript: Configuração, tsx e ts-node na Prática
O que Todo Dev Deve Saber sobre Node.js com TypeScript: Configuração, tsx e ts-node na Prática

Node.js com TypeScript: Configuração, tsx e ts-node na Prática TypeScript é u...

TypeScript Compiler API: Parsear, Transformar e Gerar Código na Prática
TypeScript Compiler API: Parsear, Transformar e Gerar Código na Prática

Introdução à TypeScript Compiler API A TypeScript Compiler API é um conjunto...