Generics em JavaScript: para que servem e como usar
No mundo do desenvolvimento moderno com JavaScript e Node.js, o conceito de Generics (genéricos, ou tipos genéricos) é cada vez mais comum. Embora o JavaScript nativo não suporte tipagem estática, ferramentas como TypeScript e JSDoc permitem incorporar mecanismos poderosos de programação genérica. Neste artigo, vamos entender para que servem os Generics, como funcionam no contexto do Node.js e como usá-los corretamente.
O que são Generics e para que servem?
Generics são um mecanismo que permite criar componentes (funções, classes, interfaces) que trabalham com diferentes tipos de dados, mantendo a tipagem estática. Em vez de escrever a mesma função para números, strings e arrays, você escreve um único código genérico.
Principais vantagens dos Generics:
- Type safety — erros de tipo são detectados em tempo de compilação/verificação, não em runtime.
- Reutilização de código — uma única função funciona com qualquer tipo de dado.
- Legibilidade — o código se torna autodocumentável.
- Integração com IDE — autocomplete e dicas de tipo funcionam corretamente.
No Node.js, Generics são especialmente úteis ao trabalhar com bancos de dados, requisições de API, coleções de dados e utilitários.
Generics em TypeScript (forma principal)
TypeScript é uma extensão do JavaScript que adiciona tipagem estática. É no TypeScript que os Generics são implementados de forma mais completa. Vamos ver a sintaxe básica.
Função genérica simples
// Sem Generics — é necessário usar anyfunction identity(arg: any): any { return arg;}
// Com Generics — o tipo é preservadofunction identity<T>(arg: T): T { return arg;}
// Usoconst num = identity<number>(42); // tipo numberconst str = identity<string>("hello"); // tipo stringOs colchetes angulares <T> são a declaração do parâmetro de tipo. T é um nome convencional, pode ser qualquer um (frequentemente usam T, U, V).
Generics com arrays
Um dos casos mais comuns é trabalhar com arrays:
// Função que retorna o primeiro elemento de um arrayfunction getFirst<T>(arr: T[]): T | undefined { return arr[0];}
const firstNumber = getFirst([1, 2, 3]); // numberconst firstString = getFirst(["a", "b", "c"]); // stringMúltiplos tipos
É possível usar vários parâmetros de tipo:
function pair<T, U>(first: T, second: U): [T, U] { return [first, second];}
const result = pair(42, "resposta"); // tipo [number, string]Restrições (Constraints) em Generics
Às vezes é necessário restringir quais tipos podem ser usados. Para isso, utilizam-se restrições (constraints) com a palavra-chave extends.
// Função que funciona apenas com objetos que possuem a propriedade lengthfunction getLength<T extends { length: number }>(arg: T): number { return arg.length;}
getLength("hello"); // 5 — string tem lengthgetLength([1, 2, 3]); // 3 — array tem length// getLength(42); // Erro! number não tem lengthIsso é especialmente útil no Node.js ao trabalhar com buffers, strings e arrays — todos que possuem a propriedade length.
Generics em classes e interfaces
Generics funcionam muito bem não apenas com funções, mas também com classes e interfaces.
Classe genérica
Exemplo de uma pilha simples em Node.js:
class Stack<T> { private items: T[] = [];
push(item: T): void { this.items.push(item); }
pop(): T | undefined { return this.items.pop
```