Menos código, mais utilidade: entendendo funções de seta e genéricos em TypeScript
Você já escreveu uma pequena utilidade em TypeScript que precisava funcionar com números, strings e arrays? E logo se deparou com o problema: como fazer com que o tipo de dado seja flexível, mas sem que o TypeScript reclame? A resposta — genéricos (generics, ou tipos generalizados). E se você gosta de sintaxe concisa, provavelmente usa funções de seta.
Mas eis o problema: no Stack Overflow, a pergunta «What is the syntax for Typescript arrow functions with generics?» acumulou milhares de visualizações. Acontece que até desenvolvedores experientes às vezes se confundem com essa combinação aparentemente simples. Neste artigo, vamos entender como escrever corretamente funções de seta TypeScript com genéricos, analisar erros comuns e mostrar como deixar seu código mais limpo e seguro.
Prontos? Vamos lá!
O que são genéricos em funções de seta?
Para começar — uma breve introdução. Genéricos permitem criar componentes (funções, classes, interfaces) que trabalham com diferentes tipos, em vez de um único específico. Em vez de escrever funções separadas para números e strings, você escreve uma — com genérico.
Funções de seta (arrow functions) são uma sintaxe compacta para funções em JavaScript e TypeScript. Quando você as combina com genéricos, obtém uma ferramenta poderosa para criar utilidades reutilizáveis.
// Função comum com genéricofunction identity<T>(arg: T): T { return arg;}
// Função de seta com genérico — como fica?const arrowIdentity = <T>(arg: T): T => arg;Simples? Sim, mas há nuances que abordaremos a seguir.
Sintaxe básica: como declarar corretamente uma função de seta TypeScript com genéricos
A maneira mais simples e comum é especificar o parâmetro de tipo (genérico) antes dos parênteses com os argumentos:
const firstElement = <T>(arr: T[]): T | undefined => { return arr[0];};
// Usoconst num = firstElement([1, 2, 3]); // tipo: number | undefinedconst str = firstElement(['a', 'b']); // tipo: string | undefinedAqui, <T> é a declaração do genérico. TypeScript inferirá automaticamente o tipo T com base no argumento passado. Se você quiser especificar o tipo explicitamente, também funciona:
const num = firstElement<number>([1, 2, 3]); // Especificamos o tipo explicitamenteImportante: Em JSX (React), essa sintaxe pode conflitar com a marcação. Para esses casos, existem soluções alternativas (abordadas abaixo).
Vários genéricos em uma única função de seta
Você pode usar vários parâmetros de tipo:
const pair = <T, U>(first: T, second: U): [T, U] => { return [first, second];};
const result = pair(42, 'hello'); // tipo: [number, string]Isso é especialmente útil ao trabalhar com utilidades como map, reduce ou ao criar wrappers.
Problemas com JSX (React) e suas soluções
Se você escreve em React com TypeScript, notará que a sintaxe <T>(arg) pode ser interpretada pelo compilador como uma tag JSX. Esse é o problema clássico que fez a pergunta no Stack Overflow ganhar tanta popularidade.
Existem duas soluções principais:
1. Usar extends (mesmo quando não necessário)
Adicione extends unknown ou extends Record<string, never>:
const myFunction = <T extends unknown>(arg: T): T => { return arg;};Isso indica explicitamente ao TypeScript que <T> é um genérico, e não JSX.
2. Usar sintaxe com vírgula
Em alguns casos, adicionar uma vírgula após o genérico ajuda:
const myFunction = <T,>(arg: T):