Desafio TypeScript

Filtro tipado com guard reutilizável

Enunciado

Dada uma lista de itens que podem ser Produto ou Servico, crie um type guard isProduto e use-o para filtrar apenas os produtos. Depois, calcule o valor total dos produtos (preço * quantidade) e imprima. Defina as interfaces Produto (com tipo: 'produto', preco: number, quantidade: number) e Servico (com tipo: 'servico', descricao: string).

Requisitos

  • O type guard isProduto deve ter assinatura (item: Produto | Servico): item is Produto.
  • Use Array.prototype.filter com o guard para obter um array de Produto[].
  • Calcule e imprima o valor total dos produtos no formato Total: R$ X.

Código inicial

interface Produto {
  tipo: 'produto';
  preco: number;
  quantidade: number;
}

interface Servico {
  tipo: 'servico';
  descricao: string;
}

type Item = Produto | Servico;

// implemente isProduto e o cálculo

const itens: Item[] = [
  { tipo: 'produto', preco: 10, quantidade: 2 },
  { tipo: 'servico', descricao: 'Consultoria' },
  { tipo: 'produto', preco: 5, quantidade: 3 },
];

Saída esperada

Total: R$ 35
Ver dica

O guard pode verificar item.tipo === 'produto'. No filter, use itens.filter(isProduto) e depois reduce.

Mostrar solução
interface Produto {
  tipo: 'produto';
  preco: number;
  quantidade: number;
}

interface Servico {
  tipo: 'servico';
  descricao: string;
}

type Item = Produto | Servico;

function isProduto(item: Item): item is Produto {
  return item.tipo === 'produto';
}

const itens: Item[] = [
  { tipo: 'produto', preco: 10, quantidade: 2 },
  { tipo: 'servico', descricao: 'Consultoria' },
  { tipo: 'produto', preco: 5, quantidade: 3 },
];

const produtos: Produto[] = itens.filter(isProduto);
const total = produtos.reduce((acc, p) => acc + p.preco * p.quantidade, 0);
console.log(`Total: R$ ${total}`);

Passo a passo

  1. Definimos as interfaces Produto e Servico com a propriedade discriminante tipo.
  2. Criamos o type guard isProduto que verifica item.tipo === 'produto' e retorna item is Produto.
  3. Declaramos o array itens com elementos dos dois tipos.
  4. Usamos itens.filter(isProduto). O TypeScript reconhece o predicado e o array resultante é Produto[].
  5. Calculamos o total com reduce, multiplicando preco * quantidade de cada produto.
  6. Imprimimos o total formatado.

Por que funciona

O type guard isProduto informa ao compilador que, quando retorna true, o item é Produto. O método filter aceita um predicado de tipo e, por isso, o array resultante é tipado como Produto[]. Assim, podemos acessar preco e quantidade sem erros de compilação.

Erros comuns

  • Não usar o guard no filter: itens.filter(item => item.tipo === 'produto') retorna Item[], e acessar p.preco gera erro.
  • Esquecer o is no retorno: function isProduto(item: Item): boolean não estreita o tipo no filter.
  • Usar instanceof em interfaces: interfaces não existem em runtime; use a propriedade discriminante.

Outra forma de resolver

Usar um type predicate inline no filter:

const produtos = itens.filter((item): item is Produto => item.tipo === 'produto');

Isso é útil quando o guard não será reutilizado, mas menos legível.

Saída esperada

Total: R$ 35