Generics em TypeScript: para que servem e como usar na prática

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Introdução: o que são Generics e por que são importantes

TypeScript é uma linguagem fortemente tipada que adiciona ao JavaScript um poderoso sistema de tipos. No entanto, a tipagem estrita às vezes dificulta a escrita de código flexível e reutilizável. É aqui que entram em cena os Generics (genéricos, tipos genéricos).

Generics são uma ferramenta que permite criar componentes (funções, classes, interfaces) que funcionam com diferentes tipos de dados, sem perder o controle de tipos. Imagine que você está escrevendo uma função que deve funcionar com números, strings e arrays. Sem genéricos, você teria que usar any (o que anula todo o benefício da tipagem) ou criar funções separadas para cada tipo de dado. Os Generics resolvem esse problema de forma elegante e segura.

Neste artigo, vamos explorar para que servem os genéricos em TypeScript e, o mais importante, como usá-los na prática — desde funções simples até classes complexas e restrições de tipos.



Parte principal



1. Generics em funções: da teoria à prática

O exemplo mais simples e comum de uso de genéricos são funções que recebem e retornam dados do mesmo tipo. Vamos considerar uma tarefa clássica: uma função que retorna o primeiro elemento de um array.



Abordagem ruim (sem Generics):

// Exemplo com any — perdemos a informação do tipofunction getFirst(arr: any[]): any {    return arr[0];}

const num = getFirst([1, 2, 3]); // tipo num — any, não sabemos que é um número


Abordagem correta (com Generics):

// Usamos o tipo genérico Tfunction getFirst<T>(arr: T[]): T {    return arr[0];}

const num = getFirst([1, 2, 3]); // tipo num — numberconst str = getFirst(['a', 'b', 'c']); // tipo str — stringconst bool = getFirst([true, false]); // tipo bool — boolean


Como isso funciona? Declaramos um parâmetro de tipo <T> (o nome pode ser qualquer um, mas por convenção usamos T, U, V). O TypeScript infere automaticamente o tipo específico com base no argumento passado. Isso é chamado de inferência de tipos.



Você também pode especificar explicitamente o tipo ao chamar a função:

const num = getFirst<number>([1, 2, 3]); // especificação explícita do tipo


Outro exemplo útil é uma função que retorna um elemento aleatório de um array:

function getRandom<T>(items: T[]): T {    const randomIndex = Math.floor(Math.random() * items.length);    return items[randomIndex];}

const randomNumber = getRandom([10, 20, 30]); // tipo — numberconst randomName = getRandom(['Ana', 'João', 'Maria']); // tipo — string


2. Generics em interfaces e tipos

Os genéricos também funcionam muito bem com interfaces. Isso permite criar estruturas de dados flexíveis que podem armazenar valores de diferentes tipos.



Imagine que queremos descrever uma resposta de API que pode conter dados de qualquer tipo:

interface ApiResponse<T> {    status: number;    message: string;    data: T;}

// Uso para diferentes tipos de dadosconst userResponse: ApiResponse<{ id: number; name: string }> = { status: 200, message: 'Sucesso', data: { id: 1, name: 'Alex' }};

const listResponse: ApiResponse<string[]> = { status: 200, message: 'Lista recebida', data: ['maçã', 'banana', 'laranja']};


Também podemos usar genéricos com aliases de tipo (type):

type Result<T> = {    success: boolean;    value: T;    error?: string;};

const successResult: Result<number> = { success: true, value: 42 };const errorResult: Result<string> = { success: false, error: 'Erro de carregamento', value: '' };


Observe: podemos co

Blogs

Book Recommendations