Médio TypeScript

Narrowing com in e instanceof

Enunciado

Crie uma hierarquia de classes Veiculo (abstrata) com subclasses Carro e Bicicleta. Cada uma tem um método específico: Carro tem abrirPortaMalas(), Bicicleta tem tocarCampainha(). Implemente uma função usarVeiculo que recebe um Veiculo e chama o método específico usando narrowing com instanceof.

Requisitos

  • Usar instanceof para verificar o tipo concreto.
  • A função deve aceitar qualquer Veiculo.
  • Chamar o método correto para cada subclasse.

Código inicial

abstract class Veiculo {
  abstract mover(): void;
}

class Carro extends Veiculo {
  mover() { console.log('Carro andando'); }
  abrirPortaMalas() { console.log('Porta-malas aberto'); }
}

class Bicicleta extends Veiculo {
  mover() { console.log('Bicicleta pedalando'); }
  tocarCampainha() { console.log('Ring ring!'); }
}

function usarVeiculo(veiculo: Veiculo) {
  // Seu código aqui
}

usarVeiculo(new Carro());
usarVeiculo(new Bicicleta());

Saída esperada

Carro andando
Porta-malas aberto
Bicicleta pedalando
Ring ring!
Ver dica

Use if (veiculo instanceof Carro) para acessar abrirPortaMalas(). Não esqueça de chamar mover() antes.

Mostrar solução
abstract class Veiculo {
  abstract mover(): void;
}

class Carro extends Veiculo {
  mover() { console.log('Carro andando'); }
  abrirPortaMalas() { console.log('Porta-malas aberto'); }
}

class Bicicleta extends Veiculo {
  mover() { console.log('Bicicleta pedalando'); }
  tocarCampainha() { console.log('Ring ring!'); }
}

function usarVeiculo(veiculo: Veiculo) {
  veiculo.mover(); // método comum
  if (veiculo instanceof Carro) {
    veiculo.abrirPortaMalas(); // veiculo é Carro
  } else if (veiculo instanceof Bicicleta) {
    veiculo.tocarCampainha(); // veiculo é Bicicleta
  }
}

usarVeiculo(new Carro());
usarVeiculo(new Bicicleta());

Passo a passo

  1. Definimos a classe abstrata Veiculo com o método abstrato mover.
  2. Carro e Bicicleta estendem Veiculo e implementam mover, além de métodos próprios.
  3. A função usarVeiculo recebe um Veiculo e chama mover() (disponível em todos).
  4. Usamos instanceof Carro para verificar se é um carro; se sim, chamamos abrirPortaMalas().
  5. Caso contrário, verificamos instanceof Bicicleta e chamamos tocarCampainha().
  6. O TypeScript estreita o tipo dentro de cada bloco.

Por que funciona

O operador instanceof verifica se o objeto é uma instância de uma classe específica, checando a cadeia de protótipos. O TypeScript reconhece essa verificação como um type guard, refinando o tipo de veiculo para a subclasse correspondente dentro do bloco.

Erros comuns

  • Tentar chamar abrirPortaMalas() fora do if:
    veiculo.abrirPortaMalas(); // Erro: Property 'abrirPortaMalas' does not exist on type 'Veiculo'
  • Esquecer de importar ou definir as classes antes de usar.
  • Usar instanceof com tipos que não são classes (interfaces não existem em runtime).

Outra forma de resolver

Usar uma propriedade discriminante (ex: tipo: 'carro' | 'bicicleta') e verificar com igualdade. É preferível quando não se quer depender de classes concretas ou se trabalha com objetos simples.

Saída esperada

Carro andando
Porta-malas aberto
Bicicleta pedalando
Ring ring!