TypeScript: O Guia Definitivo
Pare de corrigir bugs em produção. Aprenda a tipagem estática e descubra por que os maiores projetos do mundo não usam mais JavaScript puro.
1. TypeScript: A Evolução Necessária
Para entender o TypeScript, precisamos olhar para a história. O JavaScript foi criado em 1995 em apenas 10 dias. O objetivo dele era simples: fazer pequenas animações e validações de formulário em sites estáticos.
Ninguém imaginava que, 20 anos depois, usaríamos essa mesma linguagem para criar sistemas bancários, jogos 3D e inteligências artificiais. O JavaScript cresceu, mas sua base continuou "frágil" para projetos grandes. Foi aí que a Microsoft interveio.
1.1. O Conceito de "Superset" (Superconjunto)
Muitos acham que TypeScript é uma linguagem totalmente nova. Não é. Ele é um Superset do JavaScript.
Imagine círculos concêntricos:
- Círculo Menor (JavaScript): É a linguagem padrão que roda no navegador.
- Círculo Maior (TypeScript): Engloba todo o JavaScript e adiciona uma camada extra de funcionalidades (Tipagem, Interfaces, Enums).
A Regra de Ouro: Todo código JavaScript válido é automaticamente um código TypeScript válido. Você pode pegar um arquivo.js, mudar a extensão para.ts, e ele funcionará (embora sem os benefícios da tipagem ainda).
1.2. Por que ele foi criado? (A Dor da Escala)
A Microsoft criou o TypeScript em 2012 (liderada por Anders Hejlsberg, o pai do C#) para resolver um problema específico: A Falta de Escalabilidade do JS.
No JavaScript puro (Dinâmico e Fracamente Tipado), você tem liberdade total, o que é perigoso em times grandes:
- Você não sabe o que uma função espera receber (é um número? uma string? um objeto?).
- Você só descobre erros quando o código roda no navegador do cliente (Runtime Error).
- O autocompletar (IntelliSense) das IDEs é limitado porque o editor não sabe o tipo das variáveis.
O TypeScript traz a disciplina de linguagens como Java e C# para o mundo Web, permitindo que equipes gigantes trabalhem no mesmo código sem quebrar as funcionalidades uns dos outros.
1.3. Como funciona "Por Baixo do Capô"?
Aqui está o segredo que confunde iniciantes: O navegador NÃO entende TypeScript. O Chrome, Firefox e Edge só leem JavaScript.
Então, como funciona? O TypeScript precisa de um processo chamado Transpilação.
- Desenvolvimento: Você escreve código em arquivos
.tscom tipagem forte e segurança. - Compilação (TSC): O compilador do TypeScript (
tsc) lê seu código. Se houver erros de tipo, ele trava e te avisa. - Transpilação: Se não houver erros, ele remove toda a tipagem e gera um arquivo
.jslimpo. - Execução: O navegador roda esse arquivo
.jsgerado.
Ou seja: O TypeScript é uma ferramenta de Desenvolvimento. Ele existe para ajudar o programador a não cometer erros. No final, tudo vira JavaScript.
2. O Sistema de Tipos: Dominando a Base
No JavaScript, as variáveis são "caixas abertas": você pode colocar um gato dentro, tirar o gato e colocar um tijolo. No TypeScript, as caixas têm etiquetas. Se a caixa diz "Gato", você jamais poderá colocar um "Tijolo" lá dentro.
2.1. O Duelo: Tipagem Explícita vs. Inferência
Uma dúvida comum é: "Eu preciso colocar : string em todas as minhas variáveis?". A resposta é: Não.
O TypeScript possui um recurso poderoso chamado Type Inference (Inferência de Tipo). Ele analisa o valor que você atribuiu e "trava" a variável automaticamente.
A) Inferência (O jeito limpo)
Use quando você já inicializa a variável com um valor. Deixe o código menos verboso.
// O TS vê as aspas e entende: "Isso é uma string para sempre"
let curso = "TypeScript";
// curso = 10; // ❌ Erro: O TS não deixa mudar para número
B) Tipagem Explícita (O jeito seguro)
Use em dois casos principais:
- Quando você cria a variável mas não atribui valor na hora.
- Em parâmetros de função (Obrigatório).
// Caso 1: Variável iniciada vazia
let resposta: string;
// ...muitas linhas depois...
resposta = "Sucesso";
// Caso 2: Parâmetros de Função
// O TS não tem como adivinhar o que 'n1' e 'n2' serão, você TEM que dizer.
function somar(n1: number, n2: number) {
return n1 + n2;
}
2.2. Guia Prático dos Tipos Essenciais
Abaixo, temos exemplos reais de como aplicar cada tipo primitivo. Copie e teste no seu editor.
🔤 String, Number e Boolean (Os Primitivos)
A base de 90% das variáveis.
// STRING: Textos, frases e Template Strings
const nomeCompleto: string = "Maria Silva";
const mensagem: string = `Bem-vinda, ${nomeCompleto}`; // Template String
// NUMBER: Inteiros e Decimais (tudo é number em JS)
const idade: number = 30;
const altura: number = 1.65;
// BOOLEAN: Verdadeiro ou Falso (Flags de controle)
const isAdmin: boolean = true;
const temPermissao: boolean = false;
⚠️ Any vs. Unknown (O Perigo e a Solução)
Aqui mora a diferença entre um júnior e um sênior.
ANY (O Vilão): Desliga o TypeScript. Aceita qualquer coisa e não avisa erros.
let variavelPerigosa: any = "Texto";
variavelPerigosa = 10; // Aceita mudar
variavelPerigosa.metodoQueNaoExiste(); // ❌ Aceita rodar (mas vai quebrar o site!)
UNKNOWN (O Herói Seguro): Aceita qualquer coisa, mas te obriga a verificar antes de usar.
let variavelSegura: unknown = "Texto";
variavelSegura = 10; // Aceita mudar
// variavelSegura.toUpperCase(); // ❌ O TS bloqueia: "Você não garantiu que é texto!"
// Passo a passo para usar Unknown:
if (typeof variavelSegura === "string") {
console.log(variavelSegura.toUpperCase()); // ✅ Agora o TS permite
}
🛑 Void vs. Never (O Retorno das Funções)
Usados para tipar o que uma função devolve.
VOID (Vazio): A função executa algo, mas não devolve nenhum valor útil (apenas undefined).
function logarMensagem(msg: string): void {
console.log("Log do Sistema: " + msg);
// Não tem 'return'
}
NEVER (Nunca): A função jamais termina sua execução com sucesso. Geralmente usada para erros fatais ou loops infinitos.
function tratarErro(mensagem: string): never {
throw new Error(mensagem);
// O script morre aqui. Ele nunca chega na linha de baixo.
}
Resumo de Estudo:
1. Use Inferência sempre que possível para limpar o código.
2. Use Explícita em parâmetros de funções.
3. Jamais useany, prefiraunknown.
4. Usevoidpara funções de efeito colateral (logs, alertas).
3. Modelagem de Dados: O Poder da Estrutura
No JavaScript, um objeto é uma bagunça. Você pode criar um usuário com `nome`, depois adicionar um `endereço` e, por fim, deletar o `nome`. O código vira uma terra sem lei.
No TypeScript, nós impomos a ordem. Antes de criar um objeto, nós desenhamos a "planta baixa" dele. Se o objeto não seguir a planta, o código não compila.
3.1. Interfaces: O Contrato do seu Código
O que é? Uma Interface é um contrato. Ela define a forma exata que um objeto deve ter.
Por que usar?
- Segurança: Impede que você esqueça um campo obrigatório (ex: esquecer a senha do usuário).
- IntelliSense: Ao digitar
usuario., o VS Code vai listar todas as propriedades disponíveis automaticamente. - Manutenção: Se você mudar o nome de um campo na Interface, o TS avisa todos os lugares do código que precisam ser atualizados.
🎓 Passo a Passo: Modelando um E-commerce
Vamos criar um sistema de produtos. Queremos garantir que todo produto tenha nome e preço, mas a descrição seja opcional.
Passo 1: Criar o Contrato (A Interface)
interface Produto {
id: number;
titulo: string;
preco: number;
// O '?' indica que este campo não é obrigatório
descricao?: string;
// 'readonly' significa que, uma vez criado, não pode mudar
readonly dataCadastro: Date;
}
Passo 2: Assinar o Contrato (Criar o Objeto)
// O TS verifica se 'meuCelular' respeita a Interface 'Produto'
const meuCelular: Produto = {
id: 1,
titulo: "iPhone 15",
preco: 5000,
dataCadastro: new Date()
// Note que não coloquei 'descricao', e o TS aceitou porque é opcional (?)
};
Passo 3: Testando as Regras
meuCelular.titulo = "iPhone 16"; // ✅ Permitido (é string)
meuCelular.preco = "Caro"; // ❌ Erro: Esperava number, recebeu string
// ❌ Erro: 'dataCadastro' é somente leitura (readonly)
// meuCelular.dataCadastro = new Date();
3.2. Union Types: O Poder do "OU" (Flexibilidade)
O que é? O operador | (pipe) funciona como um "OU". Ele diz ao TypeScript: "Esta variável pode ser X OU Y".
Por que usar? O mundo real não é perfeito. Às vezes um ID vem do banco como número (1), às vezes vem da URL como texto ("1"). Às vezes um status pode ser "sucesso" ou "falha". O Union Type modela essa incerteza de forma segura.
🎓 Tutorial Prático 1: O "ID Híbrido"
Vamos criar uma função que aceita tanto número quanto texto.
// Definimos um "Type Alias" para não repetir código
type ID = string | number;
function exibirID(codigo: ID) {
// O TS sabe que 'codigo' pode ser string ou number
console.log(`O código do produto é: ${codigo}`);
}
exibirID(10); // ✅ Funciona
exibirID("A-50"); // ✅ Funciona
exibirID(true); // ❌ Erro: Boolean não está na lista permitida
🎓 Tutorial Prático 2: Literal Types (O Semáforo)
Podemos ser ainda mais específicos. Em vez de permitir qualquer string, podemos permitir apenas palavras específicas. Isso é incrível para configurações.
// A variável só pode ter um desses 3 valores exatos
type StatusPedido = "pendente" | "aprovado" | "cancelado";
function atualizarStatus(novoStatus: StatusPedido) {
console.log(`Status alterado para: ${novoStatus}`);
}
atualizarStatus("aprovado"); // ✅ Funciona
// atualizarStatus("entregue"); // ❌ Erro: "entregue" não existe no tipo StatusPedido
Dica de Mestre (Type Guard):
Quando você usa Union Types (ex:string | number), às vezes precisa saber qual dos dois é o atual para usar métodos específicos (como.toUpperCase()que só existe em strings).
Use otypeofpara garantir:function formatar(entrada: string | number) { if (typeof entrada === "string") { return entrada.toUpperCase(); // ✅ TS sabe que aqui é string } else { return entrada.toFixed(2); // ✅ TS sabe que aqui sobrou o number } }
4. TypeScript e o DOM: A Ponte entre Código e Tela
Antes de falarmos de código, precisamos entender o conceito. DOM (Document Object Model) é a representação do seu HTML na memória do navegador.
Imagine que o HTML é a "planta arquitetônica" da casa (papel estático). O DOM é a "casa construída" (interativa). O JavaScript/TypeScript precisa manipular a casa construída, não o papel.
4.1. O Problema: A Cegueira do TypeScript
Aqui está o segredo que ninguém conta: O TypeScript NÃO lê o seu arquivo HTML.
Quando você escreve document.getElementById("meu-input"), o TypeScript não faz ideia se esse ID existe no HTML, nem se ele é um botão, uma imagem ou um campo de texto.
Por segurança, o TS assume que o elemento é um HTMLElement (um elemento genérico).
- HTMLElement (Genérico): Tem coisas básicas como
.style,.id,.click(). - HTMLInputElement (Específico): Tem tudo do genérico MAIS o
.value.
Se você tentar acessar .value em um elemento genérico, o TS grita: "Ei! Nem todo elemento HTML tem valor! E se isso for uma <div>?".
4.2. A Solução: Type Assertion (O comando 'as')
Já que o TS é cego para o HTML, você precisa ser os olhos dele. Usamos a palavra-chave as para garantir: "TypeScript, confie em mim, eu sei que esse ID pertence a um Input".
Tutorial Passo a Passo: Capturando um Valor
Cenário: Temos um campo de texto no HTML e queremos pegar o que o usuário digitou.
1. O HTML (A Tela)
<!-- Um campo de e-mail -->
<input type="email" id="email-usuario" placeholder="Digite seu e-mail">
2. O Erro (Sem Tipagem)
const email = document.getElementById("email-usuario");
// ❌ ERRO: Property 'value' does not exist on type 'HTMLElement'.
// O TS pensa: "Isso pode ser uma div, e div não tem value!"
console.log(email.value);
3. A Correção (Type Assertion)
// ✅ CORRETO: Usamos 'as HTMLInputElement'
const email = document.getElementById("email-usuario") as HTMLInputElement;
// Agora o TS sabe que é um Input e habilita o .value
console.log(email.value);
4.3. Glossário: Qual tipo devo usar?
Como saber qual HTML...Element usar? Geralmente o nome é óbvio, mas aqui está uma tabela dos mais comuns para você consultar:
| Tag HTML | Tipo no TypeScript | Propriedade Exclusiva (Exemplo) |
|---|---|---|
<input> |
HTMLInputElement |
.value, .placeholder |
<button> |
HTMLButtonElement |
.disabled |
<a> |
HTMLAnchorElement |
.href |
<img> |
HTMLImageElement |
.src, .alt |
<form> |
HTMLFormElement |
.submit() |
<div> |
HTMLDivElement |
(Apenas propriedades genéricas) |
Dica de Segurança:
O comandogetElementByIdpode retornarnull(se o ID não existir). Em projetos rigorosos, o TS pode exigir que você verifique se o elemento existe antes de usar:const btn = document.getElementById("botao") as HTMLButtonElement; if (btn) { // Só entra aqui se 'btn' não for nulo btn.disabled = true; }
5. Projeto Prático: To-Do List (Tipada e Segura)
A melhor forma de aprender TypeScript é vendo ele "salvar" seu código. Vamos refatorar uma lista de tarefas. Em JavaScript puro, você poderia acidentalmente adicionar um número na lista ou esquecer de salvar o texto. Com TypeScript, o código nem compila se você errar.
Vamos dividir a construção em 3 camadas lógicas:
- Modelagem: O Contrato de Dados (Interface).
- Visualização: O HTML (Estrutura).
- Controle: A Lógica (TypeScript).
Passo 1: A Modelagem (O Contrato)
Antes de pensar em funções ou botões, pense nos DADOS. O que compõe uma tarefa?
Criamos uma interface. Ela será o nosso "molde". Todo objeto que criarmos no futuro precisará passar nesse teste de qualidade.
// 1. O Contrato de Dados
interface Tarefa {
id: number; // Identificador único (para podermos apagar depois)
texto: string; // O conteúdo da tarefa
concluida: boolean; // Estado: true (riscada) ou false (pendente)
}
Por que fazer isso?
Se no futuro você tentar fazertarefa.concluida = "sim"(string), o TypeScript vai gritar erro. Você garante a integridade do banco de dados na memória.
Passo 2: A Estrutura (HTML)
Precisamos dos elementos na tela. Crie um arquivo index.html simples:
<!-- Simples e Limpo -->
<div class="container">
<h2>Minhas Tarefas</h2>
<div class="input-group">
<input type="text" id="taskInput" placeholder="Nova tarefa...">
<button id="btnAdd">Adicionar</button>
</div>
<ul id="lista">
<!-- As tarefas vão aparecer aqui -->
</ul>
</div>
Passo 3: A Lógica Segura (TypeScript)
Agora vamos conectar tudo. Leia os comentários no código abaixo para entender a decisão técnica de cada linha.
3.1. Seleção e Estado
Aqui usamos o Type Assertion (`as HTML...`) que aprendemos antes.
// A. Capturando elementos do DOM (A Ponte)
const inputTask = document.getElementById("taskInput") as HTMLInputElement;
const btnAdd = document.getElementById("btnAdd") as HTMLButtonElement;
const listElement = document.getElementById("lista") as HTMLUListElement;
// B. O Estado da Aplicação (Single Source of Truth)
// Dizemos: "Este é um array que SÓ aceita objetos do tipo Tarefa"
const tarefas: Tarefa[] = [];
3.2. A Função de Adicionar
function adicionarTarefa() {
// Validação básica
if (inputTask.value === "") return;
// C. Criação do Objeto
// Se eu esquecer o 'id', o TS avisa que o contrato foi quebrado!
const novaTarefa: Tarefa = {
id: Date.now(), // Gera um número único baseado no tempo (timestamp)
texto: inputTask.value,
concluida: false
};
// Adiciona ao array e atualiza a tela
tarefas.push(novaTarefa);
renderizar();
// UX: Limpa o campo e foca para digitar a próxima
inputTask.value = "";
inputTask.focus();
}
// Ouvinte de Evento
btnAdd.addEventListener("click", adicionarTarefa);
3.3. A Função de Renderizar (O Espelho)
Esta função pega o estado (Array) e o reflete na tela (HTML).
function renderizar() {
listElement.innerHTML = ""; // 1. Limpa a lista visual atual
tarefas.forEach(task => {
// 2. Cria o elemento na memória
const li = document.createElement("li");
li.innerText = task.texto;
// 3. Aplica estilo se estiver concluída
if (task.concluida) {
li.style.textDecoration = "line-through";
li.style.opacity = "0.6";
}
// 4. Funcionalidade Extra: Clique para riscar
li.addEventListener("click", () => {
task.concluida = !task.concluida; // Inverte (true <-> false)
renderizar(); // Atualiza a tela novamente
});
// 5. Joga o elemento na tela
listElement.appendChild(li);
});
}
Análise Técnica: Onde o TypeScript brilhou?
Se tivéssemos feito isso em JavaScript puro, estaríamos expostos a vários riscos que o TS eliminou:
- Autocompletar Inteligente: Ao digitar
task.dentro doforEach, o VS Code sugeriu.textoe.concluida. No JS, teríamos que decorar os nomes. - Segurança no Array: Se tentássemos fazer
tarefas.push("Comprar pão")(apenas a string), o TS impediria, pois definimos que o array espera objetos completos da interfaceTarefa. - Manipulação do DOM: Ao usar
as HTMLInputElement, garantimos que a propriedade.valueexiste, evitando erros de "undefined" em tempo de execução.
6. Conclusão: A Verdade sobre o TypeScript
Vou ser bem honesto com você: nas primeiras semanas, o TypeScript parece chato. Você vai tentar somar dois números e o VS Code vai gritar um erro vermelho. Você vai pensar: "Poxa, em JavaScript isso funcionava, por que complicar?".
Mas a virada de chave acontece quando você volta no seu código 2 meses depois. Graças à tipagem, você sabe exatamente o que aquela função calcularImposto(valor) espera receber, sem precisar ler o código inteiro de novo.
TypeScript não é sobre escrever mais código. É sobre dormir tranquilo sabendo que seu sistema não vai quebrar na mão do cliente por um erro bobo de digitação.
O Grande Desafio Final: A Trindade (Node + TS + Docker)
Lembra que criamos um container Docker no artigo passado? E agora aprendemos TypeScript? O mercado de trabalho exige que você saiba misturar os dois.
Sua missão: Criar uma API simples em Node.js usando TypeScript e fazê-la rodar dentro de um container Docker.
Parece difícil? Tente fazer sozinho primeiro. Se travar (ou se quiser conferir se fez do jeito "profissional"), clique na caixa abaixo. Eu deixei o gabarito completo para você.
👁️ Ver a Solução Completa (Gabarito)
Para isso funcionar, você precisa de 4 arquivos na mesma pasta. Aqui está a receita:
1. package.json (As Dependências)
Inicie com npm init -y e instale: npm i express e npm i -D typescript @types/express @types/node.
2. tsconfig.json (Configuração do TS)
Crie este arquivo para dizer ao TS: "Transforme o código da pasta /src para JavaScript na pasta /dist".
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true
}
}
3. src/index.ts (O Código da API)
import express, { Request, Response } from 'express';
const app = express();
const port = 3000;
app.get('/', (req: Request, res: Response) => {
res.json({
mensagem: "Funcionou! TypeScript + Docker 🐳💙",
timestamp: new Date()
});
});
app.listen(port, () => {
console.log(`Servidor rodando na porta ${port}`);
});
4. Dockerfile (A Mágica)
Aqui está o segredo: O Docker precisa compilar o TS para JS antes de rodar.
# Etapa 1: Base
FROM node:18-alpine
WORKDIR /app
# Etapa 2: Instalar Dependências
COPY package*.json ./
RUN npm install
# Etapa 3: Copiar código e COMPILAR (Build)
COPY . .
RUN npx tsc
# Etapa 4: Rodar o JavaScript gerado na pasta dist
EXPOSE 3000
CMD ["node", "dist/index.js"]
Como rodar?
docker build -t minha-api-ts .
docker run -p 3000:3000 minha-api-ts
Isso encerra nossa série de fundamentos. Agora você tem a base de HTML, CSS, JS, Docker e TypeScript. O próximo passo? Escolha um framework (React ou Next.js) e comece a construir projetos reais para o seu portfólio. Te vejo no topo! 🚀
