Aprenda / TypeScript do zero

FundamentosAula 5 de 1416 min0% da trilha

Generics

Escrever uma função/tipo que funciona com qualquer tipo, sem perder a checagem — o <T> que aparece por toda API tipada.

O problema que generics resolve

// versão sem generics — funciona, mas perde informação de tipo
function primeiroItem(lista: any[]): any {
  return lista[0];
}

const numero = primeiroItem([1, 2, 3]);
numero.toFixed(2); // TS não reclama... mas também não confirma que "numero" é number de verdade
// com generics — o tipo de entrada "vaza" pro tipo de saída
function primeiroItem<T>(lista: T[]): T {
  return lista[0];
}

const numero = primeiroItem([1, 2, 3]);        // T é inferido como number
const nome = primeiroItem(["Ana", "Bia"]);       // T é inferido como string

numero.toFixed(2);   // ok, TS sabe que é number
nome.toUpperCase();   // ok, TS sabe que é string

<T> é um "parâmetro de tipo" — um espaço reservado que só se torna um tipo concreto quando a função é chamada. É a mesma ideia de um parâmetro normal, só que pra tipo em vez de valor.

Generics em interface — o formato de uma resposta de API

interface RespostaApi<T> {
  dado: T;
  status: number;
}

const respostaUsuario: RespostaApi<{ nome: string }> = {
  dado: { nome: "Ana" },
  status: 200,
};

const respostaLista: RespostaApi<string[]> = {
  dado: ["a", "b", "c"],
  status: 200,
};

Isso evita escrever RespostaApiDeUsuario, RespostaApiDeLista, RespostaApiDeProduto... — uma interface genérica cobre todos os formatos de resposta, mudando só o que vai dentro de dado.

Restringindo o generic com extends

function pegarNome<T extends { nome: string }>(item: T): string {
  return item.nome;
}

pegarNome({ nome: "Ana", idade: 28 }); // ok — tem "nome"
pegarNome({ idade: 28 });
// Error: Property 'nome' is missing

<T extends { nome: string }> diz: "T pode ser qualquer tipo, contanto que tenha pelo menos um campo nome do tipo string." Sem isso, o TS não deixaria acessar item.nome (T poderia ser qualquer coisa, inclusive algo sem esse campo).

Exercício

Escreva uma função genérica ultimoItem que retorna o último elemento de um array de qualquer tipo.

Quiz rápido

O que <T> representa numa função genérica?
O que <T extends { nome: string }> garante?