Desafio TypeScript

Classe genérica Pilha

Enunciado

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.

Requisitos

  • A classe deve se chamar Pilha e ser genérica em T.
  • Deve ter um array privado itens: T[].
  • O construtor deve aceitar um array opcional itensIniciais?: T[].
  • push adiciona um item ao final.
  • pop remove e retorna o último item, ou undefined se vazia.
  • peek retorna o último item sem remover, ou undefined se vazia.

Código inicial

// Implemente a classe Pilha<T>
class Pilha {
  // ...
}

const pilha = new Pilha<number>([1, 2]);
pilha.push(3);
console.log(pilha.peek()); // 3
console.log(pilha.pop());  // 3
console.log(pilha.pop());  // 2
console.log(pilha.pop());  // 1
console.log(pilha.pop());  // undefined

Saída esperada

3
3
2
1
undefined
Ver dica

Use private itens: T[] e inicialize no construtor. Para pop e peek, verifique se o array está vazio antes de acessar o último elemento.

Mostrar solução
// Classe genérica que implementa uma pilha (LIFO)
class Pilha<T> {
  private itens: T[];

  // Construtor aceita um array inicial opcional
  constructor(itensIniciais?: T[]) {
    this.itens = itensIniciais ?? [];
  }

  // Adiciona um item ao topo da pilha
  push(item: T): void {
    this.itens.push(item);
  }

  // Remove e retorna o item do topo, ou undefined se vazia
  pop(): T | undefined {
    return this.itens.pop();
  }

  // Retorna o item do topo sem remover, ou undefined se vazia
  peek(): T | undefined {
    return this.itens[this.itens.length - 1];
  }
}

// Exemplo de uso
const pilha = new Pilha<number>([1, 2]);
pilha.push(3);
console.log(pilha.peek()); // 3
console.log(pilha.pop());  // 3
console.log(pilha.pop());  // 2
console.log(pilha.pop());  // 1
console.log(pilha.pop());  // undefined

Passo a passo

  1. Declaramos class Pilha<T> com o parâmetro de tipo T.
  2. A propriedade private itens: T[] armazena os elementos; é privada para encapsulamento.
  3. O construtor recebe itensIniciais?: T[] e usa ?? para inicializar com array vazio se não fornecido.
  4. O método push recebe item: T e o adiciona ao final do array com this.itens.push(item).
  5. O método pop usa this.itens.pop(), que já retorna T | undefined (undefined se vazio).
  6. O método peek acessa this.itens[this.itens.length - 1]; se o array estiver vazio, o índice é -1 e o acesso retorna undefined.
  7. No uso, new Pilha<number>([1, 2]) cria uma pilha de números; o TypeScript impede adicionar strings.

Por que funciona

A classe genérica Pilha<T> permite criar pilhas de qualquer tipo, mantendo a consistência: todos os métodos usam T. O array interno itens: T[] garante que apenas valores do tipo T sejam armazenados. A inferência de tipos também funciona: se você escrever new Pilha([1, 2]), T será inferido como number. O uso de ?? no construtor lida com a ausência do array inicial sem erros.

Erros comuns

  • Não tipar o array como T[]: private itens = [] infere any[], perdendo a segurança. Use private itens: T[].
  • Acessar this.itens[this.itens.length - 1] sem verificar vazio: em arrays vazios, o índice é -1 e o acesso retorna undefined, o que é aceitável aqui, mas se você tentar usar o valor sem checar, pode ter erros em tempo de execução. O retorno T | undefined já cobre isso.
  • Esquecer de passar o tipo genérico na instanciação: new Pilha([1, 2]) infere T como number, mas se você quiser forçar outro tipo, use new Pilha<string>().
  • Usar pop em array vazio sem tratar undefined: o retorno é T | undefined, então o compilador exige verificação antes de usar como T.

Outra forma de resolver

Você pode usar uma interface genérica para definir o contrato e implementar a classe:

interface IPilha<T> {
  push(item: T): void;
  pop(): T | undefined;
  peek(): T | undefined;
}

class Pilha<T> implements IPilha<T> { /* ... */ }

É preferível quando você quer desacoplar a implementação da interface, facilitando testes e substituições.

Saída esperada

3
3
2
1
undefined