Fácil TypeScript

Modelando uma forma geométrica

Enunciado

Crie uma união discriminada Forma que represente um círculo, um quadrado e um retângulo. Cada variante deve ter um campo tipo como discriminante e as propriedades específicas. Depois, escreva uma função area(forma: Forma): number que calcule a área usando um switch exaustivo.

Requisitos

  • A união Forma deve ter três variantes: { tipo: 'circulo'; raio: number }, { tipo: 'quadrado'; lado: number } e { tipo: 'retangulo'; base: number; altura: number }.
  • A função area deve usar switch sobre forma.tipo e retornar a área correta.
  • O default deve atribuir a variável a never para garantir exaustividade.

Código inicial

type Forma = // complete aqui

function area(forma: Forma): number {
  // implemente
}

Saída esperada

Área do círculo: 78.53981633974483
Área do quadrado: 16
Área do retângulo: 15
Ver dica

Use Math.PI * raio ** 2 para a área do círculo. Lembre-se de que o discriminante é tipo.

Mostrar solução
type Forma =
  | { tipo: 'circulo'; raio: number }
  | { tipo: 'quadrado'; lado: number }
  | { tipo: 'retangulo'; base: number; altura: number };

function area(forma: Forma): number {
  switch (forma.tipo) {
    case 'circulo':
      return Math.PI * forma.raio ** 2;
    case 'quadrado':
      return forma.lado ** 2;
    case 'retangulo':
      return forma.base * forma.altura;
    default:
      const exaustivo: never = forma;
      return exaustivo;
  }
}

console.log('Área do círculo:', area({ tipo: 'circulo', raio: 5 }));
console.log('Área do quadrado:', area({ tipo: 'quadrado', lado: 4 }));
console.log('Área do retângulo:', area({ tipo: 'retangulo', base: 5, altura: 3 }));

Passo a passo

  1. Definimos o tipo Forma como uma união de três objetos, cada um com a propriedade tipo que serve como discriminante.
  2. A função area recebe um parâmetro do tipo Forma.
  3. Dentro do switch, o TypeScript refina o tipo de forma em cada case com base no valor de forma.tipo.
  4. No case 'circulo', o compilador sabe que forma tem raio, então podemos acessá-lo com segurança.
  5. No case 'quadrado', acessamos lado sem erros.
  6. No case 'retangulo', acessamos base e altura.
  7. O default atribui forma a uma variável do tipo never. Se algum caso não for tratado, o TypeScript acusará erro de compilação.
  8. Chamamos a função com diferentes formas e imprimimos os resultados.

Por que funciona

A união discriminada permite que o TypeScript entenda que cada variante tem um conjunto distinto de propriedades. O campo tipo atua como uma etiqueta que guia o refinamento de tipo dentro do switch. Assim, o compilador garante que você só acesse propriedades que existem naquela variante específica, evitando erros em tempo de execução. O uso de never no default assegura que todos os casos foram cobertos; se um novo tipo for adicionado à união, o compilador forçará você a tratá-lo.

Erros comuns

  • Esquecer o default com never: sem ele, o TypeScript não avisa se você adicionar uma nova variante e esquecer de tratá-la. Exemplo: default: return 0; não garante exaustividade.
  • Acessar propriedades sem refinar: tentar forma.raio fora do case 'circulo' causa erro de compilação, pois raio não existe em todas as variantes.
  • Usar any ou as: isso anula a segurança de tipos. Prefira deixar o compilador inferir.
  • Discriminante não literal: se o campo tipo for tipado como string em vez de literais, o refinamento não funciona. Use tipos literais como 'circulo'.

Outra forma de resolver

Poderia usar if/else com verificação de tipo, mas o switch é mais idiomático e legível para múltiplas variantes. Exemplo:

if (forma.tipo === 'circulo') return Math.PI * forma.raio ** 2;
// ...

No entanto, o switch com never é superior para garantir exaustividade.

Saída esperada

Área do círculo: 78.53981633974483
Área do quadrado: 16
Área do retângulo: 15