Voltar para o Blog

TypeScript em 2025: Por Que 38.5% dos Desenvolvedores Escolheram e Você Deveria Também

Olá HaWkers, TypeScript está em 38.5% de popularidade segundo pesquisas de 2025, consolidando-se como uma das top 5 linguagens de programação do mundo. Mas será que é só hype ou há razões sólidas para adotar?

Neste artigo, vamos explorar por que TypeScript dominou o ecossistema JavaScript, como começar de forma prática e estratégias de migração para projetos existentes.

Por Que TypeScript Venceu a Batalha

1. Segurança de Tipos Previne Bugs Caros

// ❌ JavaScript: Bug só aparece em produção
function calculateDiscount(price, percentage) {
  return price - (price * percentage / 100);
}

// Bug: alguém passa string por engano
calculateDiscount('100', 20); // '100-NaN' = NaN 😱

// ✅ TypeScript: Erro detectado em tempo de desenvolvimento
function calculateDiscount(price: number, percentage: number): number {
  return price - (price * percentage / 100);
}

// Erro em tempo de compilação!
calculateDiscount('100', 20);
// Error: Argument of type 'string' is not assignable to parameter of type 'number'

2. IntelliSense e Autocomplete Poderosos

// TypeScript fornece autocomplete preciso
interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user' | 'guest';
  createdAt: Date;
  metadata?: {
    lastLogin?: Date;
    preferences: {
      theme: 'light' | 'dark';
      notifications: boolean;
    };
  };
}

function greetUser(user: User) {
  // IDE sugere todas propriedades disponíveis
  console.log(`Hello, ${user.name}!`);

  // Autocomplete em objetos aninhados
  if (user.metadata?.preferences.theme === 'dark') {
    // IDE sabe exatamente o que está disponível
    console.log('Dark mode enabled');
  }

  // Tipos literais previnem typos
  if (user.role === 'admim') { // Erro: 'admim' não existe
    // ...
  }
}

3. Refactoring Seguro em Grandes Codebases

// Cenário: Renomear propriedade em 100 arquivos

// Interface original
interface Product {
  productName: string;
  price: number;
}

// Refatorar para 'name' em vez de 'productName'
interface Product {
  name: string; // TypeScript mostra TODOS os lugares que quebraram
  price: number;
}

// Todos os usos são automaticamente identificados
function displayProduct(product: Product) {
  return `${product.productName} - $${product.price}`;
  // Erro: Property 'productName' does not exist on type 'Product'
  // Sugestão: Did you mean 'name'?
}

// Em JavaScript: Você descobriria esse bug em produção 💥

TypeScript na Prática: Do Básico ao Avançado

Tipos Primitivos e Básicos

// Tipos básicos
let isDone: boolean = false;
let count: number = 42;
let name: string = 'John';
let notSure: any = 4; // Evite usar 'any'!

// Arrays
let numbers: number[] = [1, 2, 3];
let strings: Array<string> = ['a', 'b', 'c'];

// Tuplas (arrays com tipos fixos)
let tuple: [string, number] = ['age', 30];

// Enum
enum Status {
  Pending = 'PENDING',
  Approved = 'APPROVED',
  Rejected = 'REJECTED'
}

const orderStatus: Status = Status.Pending;

// Union Types (tipos alternativos)
let id: string | number;
id = '123'; // OK
id = 123;   // OK
id = true;  // Erro

// Literal Types (valores específicos)
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';

function makeRequest(url: string, method: HttpMethod) {
  // method só pode ser um dos valores especificados
}

makeRequest('/api/users', 'GET');     // OK
makeRequest('/api/users', 'PATCH');   // Erro

Interfaces e Type Aliases

// Interface: Define estrutura de objetos
interface User {
  id: string;
  name: string;
  email: string;
  age?: number; // Opcional
  readonly createdAt: Date; // Readonly
}

// Extending interfaces
interface AdminUser extends User {
  permissions: string[];
  role: 'admin';
}

// Type Alias: Similar a interface, mas mais flexível
type Point = {
  x: number;
  y: number;
};

// Type para union types
type ID = string | number;

// Type para funções
type GreetFunction = (name: string) => string;

const greet: GreetFunction = (name) => `Hello, ${name}!`;

// Interface vs Type: Quando usar?
// Use Interface para objetos que podem ser estendidos
// Use Type para unions, primitivos, tuplas

// Intersection Types (combinar tipos)
type WithTimestamp = {
  createdAt: Date;
  updatedAt: Date;
};

type UserWithTimestamp = User & WithTimestamp;

const user: UserWithTimestamp = {
  id: '1',
  name: 'John',
  email: 'john@example.com',
  createdAt: new Date(),
  updatedAt: new Date()
};

Generics: Tipos Reutilizáveis

// Generic simples
function identity<T>(arg: T): T {
  return arg;
}

const num = identity<number>(42);
const str = identity<string>('hello');

// Generic com constraints
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(arg: T): T {
  console.log(arg.length); // OK, T tem length
  return arg;
}

logLength('hello');     // OK, string tem length
logLength([1, 2, 3]);   // OK, array tem length
logLength(42);          // Erro, number não tem length

// Generic em interfaces
interface Response<T> {
  data: T;
  status: number;
  message: string;
}

interface User {
  id: string;
  name: string;
}

const userResponse: Response<User> = {
  data: { id: '1', name: 'John' },
  status: 200,
  message: 'Success'
};

const usersResponse: Response<User[]> = {
  data: [
    { id: '1', name: 'John' },
    { id: '2', name: 'Jane' }
  ],
  status: 200,
  message: 'Success'
};

// Generic utility types
type Partial<T> = { [P in keyof T]?: T[P] };
type Required<T> = { [P in keyof T]-?: T[P] };
type Readonly<T> = { readonly [P in keyof T]: T[P] };
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

// Uso prático
interface TodoItem {
  id: string;
  title: string;
  completed: boolean;
  createdAt: Date;
}

// Partial: Todos campos opcionais
type TodoUpdate = Partial<TodoItem>;

function updateTodo(id: string, updates: TodoUpdate) {
  // Pode passar apenas os campos que quer atualizar
}

updateTodo('1', { completed: true }); // OK
updateTodo('2', { title: 'New title', completed: false }); // OK

// Pick: Selecionar apenas campos específicos
type TodoSummary = Pick<TodoItem, 'id' | 'title'>;

const summary: TodoSummary = {
  id: '1',
  title: 'Task'
  // completed e createdAt não são necessários
};

Casos de Uso Avançados

// 1. Discriminated Unions (Pattern Matching)
type Success<T> = {
  type: 'success';
  data: T;
};

type Error = {
  type: 'error';
  error: string;
};

type Result<T> = Success<T> | Error;

function handleResult<T>(result: Result<T>) {
  // TypeScript sabe exatamente qual tipo baseado em 'type'
  if (result.type === 'success') {
    console.log(result.data); // OK, TypeScript sabe que tem 'data'
  } else {
    console.log(result.error); // OK, TypeScript sabe que tem 'error'
  }
}

// 2. Conditional Types
type IsString<T> = T extends string ? 'yes' : 'no';

type A = IsString<string>;  // 'yes'
type B = IsString<number>;  // 'no'

// 3. Mapped Types
type Optional<T> = {
  [K in keyof T]?: T[K];
};

type RequiredUser = {
  name: string;
  email: string;
  age: number;
};

type OptionalUser = Optional<RequiredUser>;
// { name?: string; email?: string; age?: number; }

// 4. Template Literal Types (TypeScript 4.1+)
type HTTPMethod = 'GET' | 'POST' | 'PUT';
type Route = '/users' | '/products' | '/orders';

type APIEndpoint = `${HTTPMethod} ${Route}`;
// 'GET /users' | 'GET /products' | 'GET /orders' |
// 'POST /users' | 'POST /products' | 'POST /orders' |
// 'PUT /users' | 'PUT /products' | 'PUT /orders'

// 5. Type Guards
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function processValue(value: unknown) {
  if (isString(value)) {
    console.log(value.toUpperCase()); // OK, TypeScript sabe que é string
  }
}

// 6. Decorators (Experimental)
function logged(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Result:`, result);
    return result;
  };

  return descriptor;
}

class Calculator {
  @logged
  add(a: number, b: number): number {
    return a + b;
  }
}

Migração Gradual de JavaScript para TypeScript

Estratégia de Migração

// 1. Configuração inicial (tsconfig.json)
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "lib": ["ES2020", "DOM"],
    "jsx": "react-jsx",
    "strict": false, // Começar com false, habilitar gradualmente
    "allowJs": true, // Permitir arquivos .js durante migração
    "checkJs": false, // Não checar arquivos .js inicialmente
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true
  },
  "include": ["src"],
  "exclude": ["node_modules"]
}

// 2. Plano de migração
const migrationPlan = {
  'Fase 1 - Setup': [
    'Instalar TypeScript e tipos',
    'Configurar tsconfig.json',
    'Renomear 1 arquivo .js → .ts para testar',
    'Configurar build pipeline'
  ],
  'Fase 2 - Arquivos Novos': [
    'Todos arquivos novos em TypeScript',
    'Criar types.d.ts para interfaces comuns'
  ],
  'Fase 3 - Migração Gradual': [
    'Migrar módulos utilitários primeiro',
    'Migrar componentes independentes',
    'Migrar componentes com dependências',
    'Habilitar strict mode gradualmente'
  ],
  'Fase 4 - Strictness': [
    'Habilitar noImplicitAny',
    'Habilitar strictNullChecks',
    'Habilitar strictFunctionTypes',
    'Habilitar strictPropertyInitialization'
  ]
};

// 3. Tipos para bibliotecas externas
// Instalar @types packages
npm install --save-dev @types/react @types/node @types/express

// 4. Criar arquivos de declaração para código legado
// legacy.d.ts
declare module 'old-library' {
  export function doSomething(param: string): void;
}

// 5. Utility types para migração
// any-to-unknown.ts
type TODO = any; // Marca temporária, substituir depois
type FIXME = any; // Marca bugs conhecidos

// 6. Exemplo de migração progressiva
// Antes (JavaScript)
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

// Passo 1: Adicionar JSDoc (preparação)
/**
 * @param {Array<{price: number, quantity: number}>} items
 * @returns {number}
 */
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

// Passo 2: Converter para TypeScript
interface CartItem {
  price: number;
  quantity: number;
}

function calculateTotal(items: CartItem[]): number {
  return items.reduce((sum, item) => sum + item.price * item.quantity, 0);
}

// Passo 3: Adicionar validação em runtime (defense in depth)
function calculateTotal(items: CartItem[]): number {
  if (!Array.isArray(items)) {
    throw new Error('Items must be an array');
  }

  return items.reduce((sum, item) => {
    if (typeof item.price !== 'number' || typeof item.quantity !== 'number') {
      throw new Error('Invalid item format');
    }
    return sum + item.price * item.quantity;
  }, 0);
}

TypeScript com Frameworks Populares

React + TypeScript

import React, { useState, useEffect } from 'react';

// Props tipadas
interface UserCardProps {
  user: {
    id: string;
    name: string;
    email: string;
    avatar?: string;
  };
  onEdit?: (userId: string) => void;
  onDelete?: (userId: string) => void;
}

// Componente funcional tipado
export function UserCard({ user, onEdit, onDelete }: UserCardProps) {
  const [isHovered, setIsHovered] = useState<boolean>(false);

  useEffect(() => {
    console.log('User card mounted:', user.id);

    return () => {
      console.log('User card unmounted:', user.id);
    };
  }, [user.id]);

  const handleEdit = () => {
    onEdit?.(user.id); // Safe navigation operator
  };

  return (
    <div
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
      className={isHovered ? 'card-hovered' : 'card'}
    >
      {user.avatar && <img src={user.avatar} alt={user.name} />}
      <h3>{user.name}</h3>
      <p>{user.email}</p>

      <button onClick={handleEdit}>Edit</button>
      {onDelete && (
        <button onClick={() => onDelete(user.id)}>Delete</button>
      )}
    </div>
  );
}

// Hooks customizados tipados
function useUser(userId: string) {
  const [user, setUser] = useState<User | null>(null);
  const [loading, setLoading] = useState<boolean>(true);
  const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    let cancelled = false;

    async function fetchUser() {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        const data = await response.json();

        if (!cancelled) {
          setUser(data);
        }
      } catch (err) {
        if (!cancelled) {
          setError(err as Error);
        }
      } finally {
        if (!cancelled) {
          setLoading(false);
        }
      }
    }

    fetchUser();

    return () => {
      cancelled = true;
    };
  }, [userId]);

  return { user, loading, error };
}

Next.js + TypeScript

// app/page.tsx (App Router)
import { Metadata } from 'next';

export const metadata: Metadata = {
  title: 'Home Page',
  description: 'Welcome to my site'
};

interface HomePageProps {
  searchParams: { [key: string]: string | string[] | undefined };
}

export default async function HomePage({ searchParams }: HomePageProps) {
  // Server component tipado
  const data = await fetchData();

  return (
    <div>
      <h1>Welcome</h1>
      <DataDisplay data={data} />
    </div>
  );
}

// API Route tipada
// app/api/users/route.ts
import { NextRequest, NextResponse } from 'next/server';

interface UserPayload {
  name: string;
  email: string;
}

export async function POST(request: NextRequest) {
  try {
    const body: UserPayload = await request.json();

    // Validação
    if (!body.name || !body.email) {
      return NextResponse.json(
        { error: 'Missing required fields' },
        { status: 400 }
      );
    }

    // Criar usuário
    const user = await createUser(body);

    return NextResponse.json(user, { status: 201 });
  } catch (error) {
    return NextResponse.json(
      { error: 'Internal server error' },
      { status: 500 }
    );
  }
}

Ferramentas Essenciais do Ecossistema TypeScript

// 1. ESLint + TypeScript
// .eslintrc.js
module.exports = {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:@typescript-eslint/recommended-requiring-type-checking'
  ],
  parserOptions: {
    project: './tsconfig.json'
  },
  rules: {
    '@typescript-eslint/no-unused-vars': 'error',
    '@typescript-eslint/no-explicit-any': 'warn',
    '@typescript-eslint/explicit-function-return-type': 'off'
  }
};

// 2. Prettier
// .prettierrc
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "printWidth": 100
}

// 3. Husky + lint-staged (pre-commit hooks)
// package.json
{
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

// 4. ts-node para execução direta
// Rodar TypeScript diretamente sem compilar
npx ts-node script.ts

// 5. Type testing com tsd
import { expectType } from 'tsd';

// Testar se tipos estão corretos
expectType<number>(calculateTotal([{ price: 10, quantity: 2 }]));

Conclusão: Vale a Pena Investir em TypeScript?

Sim, absolutamente. TypeScript não é mais uma tendência — é o novo padrão para JavaScript profissional em 2025.

Benefícios concretos:

  • Menos bugs: Erros detectados antes de produção
  • Melhor DX: Autocomplete e refactoring seguros
  • Código mais maintível: Documentação viva via tipos
  • Carreira: 68% das vagas exigem TypeScript

Se você quer dominar JavaScript e TypeScript de forma estruturada, recomendo que dê uma olhada em outro artigo: Programação Funcional no JavaScript: Entendendo Higher-Order Functions onde você vai descobrir padrões que funcionam perfeitamente com TypeScript.

Bora pra cima! 🦅

📚 Quer Aprofundar Seus Conhecimentos em JavaScript?

Este artigo cobriu TypeScript, mas dominar JavaScript sólido é a base para tudo.

Desenvolvedores que investem em conhecimento estruturado tendem a ter mais oportunidades no mercado.

Material de Estudo Completo

Se você quer dominar JavaScript do básico ao avançado, preparei um guia completo:

Opções de investimento:

  • R$9,90 (pagamento único)

👉 Conhecer o Guia JavaScript

💡 Material atualizado com as melhores práticas do mercado

Comentários (0)

Esse artigo ainda não possui comentários 😢. Seja o primeiro! 🚀🦅

Adicionar comentário