Por que a tipagem é um superpoder, e não um fardo
\n\nImagine: você escreve código, tudo funciona, você está feliz. E então — puf! — no console um erro vermelho: Cannot read property 'name' of undefined. Familiar? Bem-vindo ao mundo do JavaScript sem tipos. Mas há uma boa notícia: o JavaScript moderno (e especialmente o TypeScript) oferece uma ferramenta poderosa — Generics (genéricos). Não é apenas "mais um recurso", mas uma maneira real de tornar o código mais limpo, seguro e compreensível.
Neste artigo, vamos explorar o que é tipagem em JavaScript, como funcionam os generics e por que você deve usá-los em todos os seus projetos. Nada de teoria seca — apenas exemplos vivos e dicas práticas.
\n\nO que são Generics e para que servem?
\n\nGenerics (tipos genéricos) são um mecanismo que permite escrever funções, classes e interfaces que trabalham com diferentes tipos de dados, mantendo uma tipagem estrita. Simplificando: você cria um código "universal" que se adapta automaticamente ao tipo de dados com o qual está trabalhando.
\n\nSem genéricos, você escreveria algo como:
\n\nfunction identity(arg: any): any {\n return arg;\n}\n\nO problema? any destrói toda a tipagem. Você perde informações sobre o tipo, e o compilador não conseguirá alertá-lo sobre erros. Com Generics é diferente:
function identity<T>(arg: T): T {\n return arg;\n}\n\n// Uso\nlet num = identity<number>(42); // tipo number\nlet str = identity<string>("Olá"); // tipo string\n\nMagia? Não, apenas tipagem inteligente. O tipo T é um placeholder que é substituído pelo tipo real quando a função é chamada.
Generics vs any: a batalha pela segurança
\n\nMuitos iniciantes perguntam: "Por que se preocupar com genéricos se posso simplesmente usar any?" A resposta é simples: segurança de tipos não é sobre "ficar bonito", mas sobre prevenir bugs em tempo de execução.
Veja este exemplo sem Generics:
\n\nfunction getLength(data: any): number {\n return data.length; // se data for número, haverá erro\n}\n\ngetLength(42); // undefined em tempo de execução\n\n\nCom genéricos, você indica explicitamente que a função trabalha com arrays ou strings:
\n\nfunction getLength<T extends { length: number }>(data: T): number {\n return data.length;\n}\n\ngetLength([1,2,3]); // 3\ngetLength("hello"); // 5\n// getLength(42); // Erro de compilação — número não tem length\n\n\nVê a diferença? O erro é capturado na hora de escrever o código, não em produção. Essa é a essência da tipagem avançada.
\n\nExemplos práticos de Generics em JavaScript
\n\nVamos analisar alguns cenários reais onde os genéricos realmente salvam o dia.
\n\n1. Funções genéricas para trabalhar com arrays
\n\nSuponha que você está escrevendo uma utilidade para extrair o primeiro elemento de um array:
\n\nfunction first<T>(arr: T[]): T | undefined {\n return arr[0];\n}\n\nconst numbers = [10, 20, 30];\nconst firstNum = first(numbers); // tipo number | undefined\n\nconst strings = ["a", "b", "c"];\nconst firstStr = first(strings); // tipo string | undefined\n\n\nSem genéricos, você teria que escrever duas funções separadas ou usar any. Com tipagem JavaScript através de Generics — uma função funciona para todos os tipos.
2. Classes genéricas para armazenamento de dados
\n\nImagine que você está criando um wrapper simples para armazenar um valor:
\n\nclass Box<T> {\n private content: T;\n\n constructor(initial: T) {\n this.content = initial;\n }\n\n getValue(): T {\n return this.content;\n }\n\n setValue(value: T): void {\n this.content = value;\n }\n}\n\n// Uso\nconst numberBox