Da trilha de TypeScript Este conceito ainda não saiu como card do dia. ir para o card de hoje

TypeScript Genéricos Intermediário

Genéricos

Genéricos em TypeScript permitem escrever funções, classes e interfaces que funcionam com vários tipos sem perder a segurança de tipos. Em vez de usar any, você declara um parâmetro de tipo (como T) que é preenchido no momento do uso. Isso mantém a flexibilidade do JavaScript com a verificação estática do TypeScript.

Por exemplo, uma função identidade<T>(arg: T): T retorna exatamente o mesmo tipo que recebeu. O compilador infere T automaticamente a partir do argumento, então você não precisa escrever identidade<number>(42) — basta identidade(42). A inferência também funciona com múltiplos parâmetros e com valores padrão.

Além de funções, você pode criar tipos genéricos como interface Caixa<T> { valor: T } ou type Par<T, U> = [T, U]. Isso permite reutilizar estruturas para diferentes tipos de dados, mantendo a relação entre eles. Valores padrão para parâmetros de tipo (<T = string>) são úteis quando o tipo não pode ser inferido e você quer um fallback seguro.

Neste card, vamos explorar funções genéricas, tipos genéricos, inferência de parâmetros e valores padrão, com exemplos práticos e exercícios progressivos.

Pontos-chave

  • Genéricos permitem escrever código reutilizável que preserva a informação de tipo.
  • A inferência de tipos deduz o parâmetro genérico a partir dos argumentos passados.
  • Tipos genéricos (interfaces, type aliases) criam estruturas flexíveis e seguras.
  • Valores padrão em parâmetros de tipo (<T = string>) evitam erros quando não há inferência.
  • Evite any: genéricos mantêm a checagem de tipos sem abrir mão da flexibilidade.
genericos.ts
// Função genérica que retorna o primeiro elemento de um array
function primeiro<T>(arr: T[]): T | undefined {
  return arr[0];
}

// Função genérica com dois parâmetros e valor padrão para o segundo tipo
function par<T, U = string>(a: T, b: U): [T, U] {
  return [a, b];
}

// Tipo genérico: uma caixa que guarda um valor de qualquer tipo
interface Caixa<T> {
  valor: T;
}

// Função que recebe uma Caixa e retorna seu conteúdo
function abrir<T>(caixa: Caixa<T>): T {
  return caixa.valor;
}

// Uso com inferência
const numeros = [10, 20, 30];
const primeiroNumero = primeiro(numeros); // T inferido como number
console.log(primeiroNumero); // 10

const texto = ["a", "b", "c"];
const primeiraLetra = primeiro(texto); // T inferido como string
console.log(primeiraLetra); // "a"

// Uso com valor padrão: U será string se não especificado
const par1 = par(1, "dois"); // T=number, U=string (inferido)
console.log(par1); // [1, "dois"]

const par2 = par(1); // erro? não, U tem padrão string, mas b é obrigatório... 
// Na verdade, o valor padrão só é usado quando o tipo não é inferido e não especificado.
// Para usar o padrão, b ainda precisa ser passado. Vamos ajustar:
function parComPadrao<T, U = string>(a: T, b?: U): [T, U | undefined] {
  return [a, b];
}
const par3 = parComPadrao(1); // U = string, b = undefined
console.log(par3); // [1, undefined]

// Caixa genérica
const caixaDeNumero: Caixa<number> = { valor: 42 };
console.log(abrir(caixaDeNumero)); // 42

const caixaDeString: Caixa<string> = { valor: "olá" };
console.log(abrir(caixaDeString)); // "olá"

Exercícios

  1. 1
    Fácil

    Função genérica de identidade

    Crie uma função genérica identidade que recebe um argumento de qualquer tipo e retorna o mesmo valor. O tipo de retorno deve ser o mesmo do argumento.

    Resolver
  2. 2
    Médio

    Par genérico com valor padrão

    Crie uma função genérica criarPar que recebe dois argumentos de tipos diferentes e retorna uma tupla [primeiro, segundo]. O segundo tipo deve ter valor padrão string caso não seja inferido. A função deve aceitar o segundo argumento como opcional.

    Resolver
  3. 3
    Desafio

    Classe genérica Pilha

    Implemente uma classe genérica Pilha<T> que armazena elementos em um array interno. A classe deve ter os métodos push(item: T): void, pop(): T | undefined e peek(): T | undefined. O construtor pode receber um array inicial opcional. Use genéricos para garantir que todos os elementos sejam do mesmo tipo.

    Resolver

Continue estudando