Introdução ao TypeScript para desenvolvedores JavaScript

1. Por que TypeScript? O problema que ele resolve

Desenvolvedores JavaScript conhecem bem a liberdade da tipagem dinâmica. Em projetos pequenos, essa flexibilidade acelera o desenvolvimento. No entanto, à medida que o código cresce, os erros silenciosos se multiplicam. Um simples "2" + 2 resulta em "22" sem qualquer aviso, e funções que esperam números recebem strings inesperadamente.

TypeScript resolve esse problema adicionando tipagem estática opcional ao JavaScript. Erros que antes apareceriam apenas em tempo de execução são capturados durante a compilação. Considere este exemplo:

// JavaScript - erro só em execução
function somar(a, b) {
    return a + b;
}
console.log(somar("5", 3)); // "53" - erro silencioso
// TypeScript - erro em tempo de compilação
function somar(a: number, b: number): number {
    return a + b;
}
console.log(somar("5", 3)); // Erro: Argumento do tipo 'string' não é atribuível ao parâmetro do tipo 'number'

Além da segurança, TypeScript oferece autocompletar inteligente em editores como VS Code, refatoração segura e documentação embutida. Grandes projetos como Angular, React (com create-react-app) e frameworks Node.js adotaram TypeScript, tornando-o habilidade essencial no mercado.

2. Configuração inicial e primeiros passos

Para começar, instale o TypeScript globalmente via npm:

npm install -g typescript

Crie um arquivo tsconfig.json na raiz do projeto:

{
    "compilerOptions": {
        "target": "ES2020",
        "module": "commonjs",
        "strict": true,
        "outDir": "./dist",
        "rootDir": "./src"
    },
    "include": ["src/**/*"]
}

Crie src/index.ts:

const saudacao: string = "Olá, TypeScript!";
console.log(saudacao);

Compile com:

tsc

O JavaScript compilado estará em dist/index.js. Para integração com Vite, use npm create vite@latest e selecione TypeScript. O VS Code já oferece suporte nativo a TypeScript, com realce de erros e IntelliSense.

3. Tipos básicos e anotações de tipo

TypeScript mantém os tipos primitivos do JavaScript com anotações:

let nome: string = "Maria";
let idade: number = 30;
let ativo: boolean = true;
let valorNulo: null = null;
let indefinido: undefined = undefined;

Arrays e tuplas:

let frutas: string[] = ["maçã", "banana"];
let numeros: Array<number> = [1, 2, 3];
let tupla: [string, number] = ["João", 25]; // posições fixas

Enums permitem conjuntos nomeados de constantes:

enum Cor {
    Vermelho = "VERMELHO",
    Verde = "VERDE",
    Azul = "AZUL"
}
let minhaCor: Cor = Cor.Verde;

Evite o tipo any sempre que possível. Ele desativa a verificação de tipos e anula os benefícios do TypeScript. Prefira unknown quando o tipo for realmente desconhecido, pois exige verificação antes do uso.

let dados: any = "texto"; // perigoso
let dadosSeguros: unknown = "texto"; // seguro, exige type guard

4. Interfaces e tipos personalizados

Interfaces definem a estrutura de objetos:

interface Usuario {
    nome: string;
    email: string;
    idade?: number; // opcional
    readonly id: number; // não pode ser alterado após criação
}

const usuario: Usuario = {
    nome: "Ana",
    email: "ana@email.com",
    id: 1
};

A diferença entre type e interface é sutil. Use interface para objetos e classes que podem ser estendidos. Use type para uniões, interseções e tipos primitivos alias:

type ID = string | number;
type Resposta = "sim" | "não" | "talvez";

interface Animal {
    nome: string;
}

interface Cachorro extends Animal {
    raca: string;
}

5. Funções tipadas e genéricos

Funções com tipos explícitos:

function saudacao(nome: string): string {
    return `Olá, ${nome}!`;
}

function configurar(
    url: string,
    timeout: number = 5000,
    headers?: Record<string, string>
): void {
    // implementação
}

Genéricos permitem criar componentes reutilizáveis com tipos flexíveis:

function primeiroElemento<T>(array: T[]): T | undefined {
    return array[0];
}

const numero = primeiroElemento([1, 2, 3]); // tipo inferido: number
const texto = primeiroElemento(["a", "b"]); // tipo inferido: string

Em React, genéricos são usados em hooks e componentes:

interface ListaProps<T> {
    itens: T[];
    renderItem: (item: T) => React.ReactNode;
}

6. Classes, herança e modificadores de acesso

TypeScript estende classes JavaScript com propriedades tipadas e modificadores:

class ContaBancaria {
    public titular: string;
    private saldo: number;
    protected numeroConta: string;

    constructor(titular: string, saldoInicial: number) {
        this.titular = titular;
        this.saldo = saldoInicial;
        this.numeroConta = this.gerarNumero();
    }

    public depositar(valor: number): void {
        this.saldo += valor;
    }

    private gerarNumero(): string {
        return Math.random().toString(36).substring(2, 10);
    }
}

class ContaPoupanca extends ContaBancaria {
    private taxaJuros: number;

    constructor(titular: string, saldoInicial: number, taxa: number) {
        super(titular, saldoInicial);
        this.taxaJuros = taxa;
    }

    public aplicarJuros(): void {
        // this.saldo não é acessível (private)
        // this.numeroConta é acessível (protected)
    }
}

Classes abstratas servem como modelos:

abstract class Forma {
    abstract calcularArea(): number;
}

class Circulo extends Forma {
    constructor(private raio: number) {
        super();
    }
    calcularArea(): number {
        return Math.PI * this.raio ** 2;
    }
}

7. TypeScript no mundo real: dicas práticas

Para migrar projetos JavaScript gradualmente, ative allowJs no tsconfig.json e use checkJs: false inicialmente. Adicione tipos gradualmente, começando pelos módulos mais críticos.

Para bibliotecas sem tipos, instale declarações de tipos:

npm install --save-dev @types/express
npm install --save-dev @types/lodash

Configure ESLint com regras TypeScript:

npm install --save-dev eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin

Ative source maps no tsconfig.json:

{
    "compilerOptions": {
        "sourceMap": true
    }
}

Isso permite depurar o código TypeScript original no navegador ou Node.js, mesmo após a compilação.

Referências