O Que São Tipos Genéricos no TypeScript?
Tipos genéricos no TypeScript são uma forma de criar componentes reutilizáveis que funcionam com vários tipos de dados, garantindo a segurança de tipos e a flexibilidade. Eles permitem que você defina uma função, classe ou interface que possa operar em diferentes tipos sem perder a verificação de tipos.
Os genéricos são amplamente usados em coleções, estruturas de dados e funções utilitárias, oferecendo uma maneira poderosa de lidar com tipos de forma mais abstrata, mas ainda assim segura.
Conceito de Tipos Genéricos
Os genéricos são definidos usando a sintaxe de "T
" ou outra letra representativa. Isso indica que o tipo específico será determinado no momento em que a função, classe ou interface for usada.
Veja um exemplo simples de função genérica:
function identidade(valor: T): T {
return valor;
}
Essa função identidade
aceita um valor do tipo T
(qualquer tipo) e retorna esse mesmo valor. O tipo exato de T
será inferido com base no argumento passado no momento da chamada.
Exemplo Prático: Função Genérica
Uma função genérica pode ser usada com diferentes tipos de valores, garantindo a tipagem correta para cada chamada.
Exemplo com Diferentes Tipos
const numero = identidade(42);
const texto = identidade('Olá');
const booleano = identidade(true);
console.log(numero); // Saída: 42
console.log(texto); // Saída: Olá
console.log(booleano); // Saída: true
Na chamada da função identidade
, estamos especificando explicitamente os tipos number
, string
e boolean
. O TypeScript garante que o tipo correto seja inferido e verificado em tempo de compilação.
Usando Genéricos com Arrays
Os tipos genéricos são extremamente úteis quando lidamos com arrays ou coleções de dados. Podemos garantir que todos os elementos do array sejam do mesmo tipo.
Exemplo de Função Genérica com Arrays
function primeiroElemento(array: T[]): T | undefined {
return array.length > 0 ? array[0] : undefined;
}
const numeros = [1, 2, 3, 4];
const nomes = ['Ana', 'Bruno', 'Carlos'];
console.log(primeiroElemento(numeros)); // Saída: 1
console.log(primeiroElemento(nomes)); // Saída: Ana
A função primeiroElemento
recebe um array de qualquer tipo T
e retorna o primeiro elemento desse array. O TypeScript garante que o retorno seja do mesmo tipo do array fornecido.
Tipos Genéricos com Interfaces
Interfaces também podem ser genéricas, permitindo que você crie estruturas de dados que funcionam com diferentes tipos.
Exemplo de Interface Genérica
interface Par {
primeiro: T;
segundo: U;
}
const coordenadas: Par = { primeiro: 10, segundo: 20 };
const nomeIdade: Par = { primeiro: 'Mauro', segundo: 30 };
console.log(coordenadas); // Saída: { primeiro: 10, segundo: 20 }
console.log(nomeIdade); // Saída: { primeiro: 'Mauro', segundo: 30 }
A interface Par
é genérica e aceita dois tipos diferentes, T
e U
. Com isso, podemos criar objetos com pares de diferentes tipos, mantendo a verificação de tipos em tempo de compilação.
Classes Genéricas no TypeScript
As classes também podem ser definidas como genéricas, permitindo a criação de estruturas de dados reutilizáveis para diferentes tipos.
Exemplo de Classe Genérica
class Caixa {
private conteudo: T;
constructor(conteudo: T) {
this.conteudo = conteudo;
}
obterConteudo(): T {
return this.conteudo;
}
}
const caixaNumero = new Caixa(123);
const caixaTexto = new Caixa('Olá');
console.log(caixaNumero.obterConteudo()); // Saída: 123
console.log(caixaTexto.obterConteudo()); // Saída: Olá
A classe Caixa
é genérica e aceita qualquer tipo T
. Isso permite que a mesma estrutura seja reutilizada para armazenar e manipular diferentes tipos de dados, mantendo a segurança de tipos.
Restrições em Tipos Genéricos
Você pode adicionar restrições aos tipos genéricos usando a palavra-chave extends
. Isso garante que o tipo genérico herde de uma interface ou tipo específico, adicionando mais controle à tipagem.
Exemplo com Restrições
interface ComNome {
nome: string;
}
function exibirNome(objeto: T): void {
console.log(`Nome: ${objeto.nome}`);
}
const pessoa = { nome: 'João', idade: 25 };
exibirNome(pessoa); // Saída: Nome: João
Neste exemplo, o tipo genérico T
é restrito a objetos que implementam a interface ComNome
. Isso garante que o objeto passado para a função exibirNome
tenha a propriedade nome
.
Boas Práticas com Tipos Genéricos
- Use Genéricos para Criar Código Reutilizável: Genéricos são ideais para criar funções e classes reutilizáveis, que funcionam com diferentes tipos de dados, garantindo a flexibilidade do código.
- Adicione Restrições Sempre Que Possível: Quando necessário, adicione restrições aos tipos genéricos para garantir que os dados atendam a critérios específicos, mantendo a segurança de tipos.
- Prefira Inferência de Tipos: O TypeScript pode inferir tipos automaticamente em muitas situações. Prefira deixar que o compilador infira os tipos quando possível, para simplificar o código.
- Mantenha a Simplicidade: Evite tornar genéricos excessivamente complexos. Mantenha as definições de tipos simples e claras para garantir a legibilidade do código.
Conclusão
Tipos genéricos no TypeScript são uma poderosa ferramenta para criar código flexível e reutilizável, sem sacrificar a segurança de tipos. Eles permitem que você crie funções, classes e interfaces que possam operar em diferentes tipos de dados, garantindo a consistência e previsibilidade. Ao usar genéricos de maneira eficaz, você melhora a manutenibilidade e a clareza do seu código, tornando-o mais escalável e menos propenso a erros.