Introdução: Por que algoritmos e estruturas de dados são importantes em TypeScript?
No desenvolvimento web moderno, o TypeScript ocupa uma posição de liderança, oferecendo tipagem estática e ferramentas poderosas para criar aplicações escaláveis. No entanto, mesmo o código mais elegante não será eficiente sem a compreensão dos fundamentais algoritmos e estruturas de dados. Este artigo é o seu guia para o mundo do código otimizado em TypeScript. Vamos analisar as principais estruturas de dados, mostrar como implementá-las considerando os tipos e examinar algoritmos clássicos. Após a leitura, você será capaz de escrever código não apenas funcional, mas também rápido e previsível.
1. Estruturas de dados básicas com tipagem
O TypeScript permite definir estritamente quais tipos de dados podem ser armazenados em uma estrutura. Isso previne erros em tempo de compilação. Vamos considerar três estruturas fundamentais.
1.1. Pilha (Stack) — princípio LIFO
A pilha funciona com o princípio "último a entrar, primeiro a sair" (Last In, First Out). É ideal para tarefas onde é necessário desfazer ações (por exemplo, histórico do navegador).
// Implementação de uma pilha com genérico Tclass Stack<T> { private items: T[] = [];
push(item: T): void { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
peek(): T | undefined { return this.items[this.items.length - 1]; }
isEmpty(): boolean { return this.items.length === 0; }}
// Exemplo de usoconst numberStack = new Stack<number>();numberStack.push(10);numberStack.push(20);console.log(numberStack.pop()); // 201.2. Fila (Queue) — princípio FIFO
A fila funciona com o princípio "primeiro a entrar, primeiro a sair" (First In, First Out). É usada para processar tarefas na ordem em que chegam, por exemplo, em sistemas de filas de mensagens.
class Queue<T> { private items: T[] = [];
enqueue(item: T): void { this.items.push(item); }
dequeue(): T | undefined { return this.items.shift(); }
front(): T | undefined { return this.items[0]; }
size(): number { return this.items.length; }}
// Exemplo com tipo personalizadointerface Task { id: number; name: string;}const taskQueue = new Queue<Task>();taskQueue.enqueue({ id: 1, name: 'Processar pedido' });taskQueue.enqueue({ id: 2, name: 'Enviar email' });1.3. Lista encadeada (Linked List)
A lista encadeada consiste em nós, cada um referenciando o próximo. Ao contrário de um array, a inserção e remoção de elementos no meio da lista não requer o deslocamento de todos os elementos.
class ListNode<T> { value: T; next: ListNode<T> | null = null;
constructor(value: T) { this.value = value; }}
class LinkedList<T> { private head: ListNode<T> | null = null;
add(value: T): void { const newNode = new ListNode(value); if (!this.head) { this.head = newNode; return; } let current = this.head; while (current.next) { current = current.next; } current.next = newNode; }
print(): void { let current = this.head; while (current) { console.log(current.value); current = current.next; } }}
// Usoconst list = new LinkedList<string>();list.add('TypeScript');list.add('Python');list.print(); // TypeScript, Python2. Algoritmos de ordenação e busca
Ordenar dados é uma das operações mais comuns. O TypeScript permite criar funções universais que funcionam com qualquer tipo de dado.
2.1. Quick Sort
Um dos algoritmos mais eficientes, com