Desafio TypeScript

Inferência com `as const` e função genérica

Enunciado

Crie um array const com os estados 'ativo', 'inativo' e 'pendente' usando as const. Escreva uma função genérica primeiro<T>(lista: readonly T[]): T que retorna o primeiro elemento. Use a função para obter o primeiro estado e imprima o valor e seu tipo literal. Depois tente atribuir um valor inválido a uma variável anotada com o tipo do primeiro estado e comente a linha que causaria erro.

Requisitos

  • O array deve ser declarado com as const e conter exatamente os três estados.
  • A função primeiro deve ser genérica com parâmetro readonly T[] e retorno T.
  • Imprimir o primeiro estado e uma mensagem indicando que o tipo é literal.
  • Incluir um comentário mostrando a linha que causaria erro de tipo.

Código inicial

const estados = ['ativo', 'inativo', 'pendente'] as const;

function primeiro<T>(lista: readonly T[]): T {
  // implemente
}

// use a função e imprima

Saída esperada

Primeiro estado: ativo
Tipo literal preservado: ativo
Ver dica

Com as const, o array vira uma tupla de literais. O genérico T será inferido como o tipo do primeiro elemento.

Mostrar solução
// `as const` transforma o array em tupla readonly de literais
const estados = ['ativo', 'inativo', 'pendente'] as const;

// Função genérica: T é inferido a partir do array
function primeiro<T>(lista: readonly T[]): T {
  return lista[0];
}

// O tipo de `primeiroEstado` é 'ativo' (literal)
const primeiroEstado = primeiro(estados);

console.log(`Primeiro estado: ${primeiroEstado}`);
console.log(`Tipo literal preservado: ${primeiroEstado}`);

// A linha abaixo causaria erro de compilação:
// primeiroEstado = 'inativo'; // erro: 'inativo' não é atribuível a 'ativo'

Passo a passo

  1. const estados = ['ativo', 'inativo', 'pendente'] as const — o as const faz o compilador tratar o array como tupla readonly de literais, sem widening para string[].
  2. function primeiro<T>(lista: readonly T[]): T — declara um genérico T e aceita um array readonly de T, retornando T.
  3. return lista[0] — retorna o primeiro elemento; o tipo de retorno é T.
  4. const primeiroEstado = primeiro(estados) — o compilador infere T como 'ativo', pois o array tem literais.
  5. Os console.log mostram o valor e confirmam que o literal foi preservado.
  6. A linha comentada demonstra que atribuir outro literal causaria erro, provando que o tipo é 'ativo'.

Por que funciona

as const impede o widening: sem ele, o array seria string[] e T seria string. Com ele, cada elemento tem tipo literal e a tupla é readonly. A função genérica preserva o tipo do elemento, então primeiroEstado é 'ativo', não string. Isso é essencial para uniões de literais e APIs type-safe.

Erros comuns

  • Esquecer as const: const estados = ['ativo', 'inativo']; → tipo string[], primeiroEstado vira string.
  • Declarar function primeiro<T>(lista: T[]): T sem readonly → erro ao passar tupla readonly: readonly ['ativo', ...] não é atribuível a T[].
  • Tentar reatribuir primeiroEstado = 'inativo' sem comentar → erro de compilação, pois o tipo é o literal 'ativo'.

Outra forma de resolver

Se quiséssemos aceitar arrays mutáveis também, poderíamos usar Array<T> em vez de readonly T[], mas perderíamos a compatibilidade com tuplas readonly. A versão com readonly é mais flexível e segura.

Saída esperada

Primeiro estado: ativo
Tipo literal preservado: ativo