O que são Generics em JavaScript e para que servem?
\n\nImagine que você está escrevendo uma função que deve funcionar com diferentes tipos de dados — números, strings, objetos. Sem Generics (genéricos), você teria que duplicar o código ou usar any, perdendo o controle sobre os tipos. Generics são um mecanismo que permite criar componentes (funções, classes, interfaces) que funcionam com qualquer tipo de dado, mas mantendo a informação sobre o tipo.
Embora JavaScript seja uma linguagem de tipagem dinâmica, no ecossistema Node.js é amplamente utilizado o TypeScript, que adiciona tipagem estática. É no TypeScript que os genéricos se revelam em toda a sua potência. No entanto, compreender seus princípios é útil também para JavaScript puro — especialmente se você escreve bibliotecas ou trabalha com frameworks modernos.
\n\nNeste artigo, abordaremos:
\n- \n
- Para que servem os Generics \n
- Como declarar e usar genéricos em TypeScript \n
- Exemplos para Node.js (API, processamento de dados) \n
- Limitações e melhores práticas \n
Fundamentos dos Generics: sintaxe e exemplos básicos
\n\nPrimeiro genérico: função identity
\n\nO exemplo clássico é uma função que retorna exatamente o que recebeu como entrada:
\n\n// Sem Generics\nfunction identity(value: any): any {\n return value;\n}\n\n// Com Generics\nfunction identity<T>(value: T): T {\n return value;\n}\n\n// Uso\nconst num = identity<number>(42); // num — number\nconst str = identity<string>('hello'); // str — string\n\n\nO símbolo <T> é o parâmetro de tipo. Você pode nomeá-lo como quiser (T, U, Item), mas a tradição recomenda usar T (de Type).
Genéricos com múltiplos parâmetros
\n\nÀs vezes é necessário trabalhar com vários tipos simultaneamente:
\n\nfunction pair<T, U>(first: T, second: U): [T, U] {\n return [first, second];\n}\n\nconst result = pair<number, string>(1, 'um');\n// result: [number, string]\n\n\nGenéricos em arrow functions (Node.js)
\n\nNo React ou em utilitários, é comum usar arrow functions com genéricos:
\n\nconst wrapInArray = <T>(item: T): T[] => {\n return [item];\n};\n\nconst arr = wrapInArray('teste'); // arr: string[]\n\n\nAplicação de Generics em projetos reais com Node.js
\n\nManipuladores de API com respostas tipadas
\n\nImagine que você está escrevendo uma função universal para requisições a APIs. Sem genéricos, você perderia os tipos dos dados:
\n\n// Ruim: any\nasync function fetchData(url: string): Promise<any> {\n const response = await fetch(url);\n return response.json();\n}\n\n// Bom: Generics\nasync function fetchData<T>(url: string): Promise<T> {\n const response = await fetch(url);\n return response.json();\n}\n\n// Uso\ninterface User {\n id: number;\n name: string;\n}\n\nconst user = await fetchData<User>('/api/user/1');\nconsole.log(user.name); // TypeScript sabe que name é string\n\n\nColeções de dados tipadas
\n\nAo trabalhar com bancos de dados (por exemplo, via Prisma ou Mongoose), os genéricos ajudam a criar repositórios universais:
\n\nclass Repository<T extends { id: number }> {\n private items: T[] = [];\n\n add(item: T): void {\n this.items.push(item);\n }\n\n getById(id: number): T | undefined {\n return this.items.find(item => item.id === id);\n }\n}\n\n// Uso\ninterface Product {\n id: number;\n title: string;\n price: number;\n}\n\nconst productRepo = new Repository<Product>();\nproductRepo.add({ id: 1, title: 'Notebook', price: 1000 });\nconst laptop = productRepo.getById(1);\n\n\nWrappers para tratamento de erros
\n\nFunção universal para