Aula 06 – Generics (Programação Genérica) 📦
Objetivo
Nesta aula, desvendaremos o poder dos Generics. Você aprenderá a criar códigos altamente reutilizáveis e flexíveis que mantêm a segurança de tipos, independentemente do dado que estão processando.
1. O que são Generics? 🧬
Generics permitem que componentes (funções, classes ou interfaces) funcionem com uma variedade de tipos, ao invés de apenas um. Pense neles como "parâmetros para tipos".
O problema sem Generics
function retornarPrimeiro(lista: any[]): any {
return lista[0];
}
// ❌ Perdemos a informação do tipo original
A solução com Generics
function retornarPrimeiro<T>(lista: T[]): T {
return lista[0];
}
const n = retornarPrimeiro([1, 2, 3]); // n é inferido como number
const s = retornarPrimeiro(["a", "b"]); // s é inferido como string
2. Constraints (Restrições) ⛓️
Nem sempre queremos que o Generic aceite "qualquer" tipo. Podemos restringir o que ele aceita usando a palavra-chave extends.
interface TemComprimento {
length: number;
}
function logComprimento<T extends TemComprimento>(obj: T) {
console.log(obj.length);
}
logComprimento("Olá"); // ✅ OK (string tem length)
logComprimento([1, 2]); // ✅ OK (array tem length)
// logComprimento(10); // ❌ Erro: number não tem length
3. Classes Genéricas 🏗️
Classes também podem se beneficiar dessa flexibilidade. Um exemplo comum são as Pilhas ou Filas.
class Caixa<T> {
private conteudo?: T;
guardar(item: T) { this.conteudo = item; }
pegar() { return this.conteudo; }
}
const caixaDeNumeros = new Caixa<number>();
caixaDeNumeros.guardar(10);
4. Generics com Interfaces 📜
Podemos tornar nossas interfaces adaptáveis a diferentes tipos de resposta, muito comum em APIs.
interface RespostaAPI<T> {
data: T;
erro: string | null;
}
const respostaUsuario: RespostaAPI<{nome: string}> = {
data: { nome: "Ricardo" },
erro: null
};
5. Visualizando o Fluxo Genérico (Mermaid)
graph LR;
Entrada[Dados de Qualquer Tipo] --> Funcao{Função Genérica < T >};
Funcao --> Saída[Mantém o Tipo Original T];
style Funcao fill:#bbf,stroke:#333
6. Exercícios Práticos 📝
- Básico: Crie uma função genérica
identidade<T>que receba um argumento de tipoTe retorne exatamente esse argumento. - Básico: Crie um array genérico de strings.
- Intermediário: Crie uma função genérica que receba dois argumentos e retorne um array contendo ambos.
- Intermediário: Use constraints para garantir que um tipo genérico possua a propriedade
id: number. - Desafio: Crie uma interface
ListaPaginada<T>que contenha um array deitens: T[]e uma propriedadetotal: number.
🚀 Mini-Projeto da Aula
Crie um Repositório Genérico de Dados.
- O repositório deve ter métodos para: salvar(item: T), obterTodos(): T[] e buscarPorId(id: number): T | undefined.
- Teste o repositório criando uma instância para Produtos e outra para Usuarios.
Próxima Aula: Vamos explorar os Utility Types e descobrir como transformar tipos existentes de forma mágica!