TypeScript generic stack and queue
Write containers that work for any element type and keep it checked.
On this page
TypeScript generics and classes.
- Write containers that work for any element type and keep it checked.
- Predict the sample output and explain the state changes.
- Complete the practice task and check the boundary cases.
The concept
A generic class takes a type parameter, so Stack<number> and Stack<string> share one implementation but cannot be mixed up. The compiler knows that pop returns a T | undefined, so you must handle the empty case before using the value. The type parameter exists only at compile time.
A stack is last-in, first-out and an array supports it with push and pop in O(1). A queue is first-in, first-out. Removing from the front of an array with shift is O(n), so this queue keeps a head index and reads from it, giving O(1) dequeue. Both classes expose a size and never leak the internal array.
Code example
class 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];
}
get size(): number {
return this.items.length;
}
}
class Queue<T> {
private items: T[] = [];
private head = 0;
enqueue(item: T): void {
this.items.push(item);
}
dequeue(): T | undefined {
if (this.head >= this.items.length) {
return undefined;
}
return this.items[this.head++];
}
get size(): number {
return this.items.length - this.head;
}
}
const history = new Stack<string>();
history.push("home");
history.push("search");
history.push("results");
console.log(history.pop(), history.peek(), history.size);
const jobs = new Queue<number>();
[10, 20, 30].forEach((job) => jobs.enqueue(job));
console.log(jobs.dequeue(), jobs.dequeue(), jobs.size);
console.log(jobs.dequeue(), jobs.dequeue());results search 2 10 20 1 30 undefined
Follow the code
Open the walkthrough to inspect the example one step at a time.
Show guided walkthrough
class 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]; } get size(): number { return this.items.length; }} class Queue<T> { private items: T[] = []; private head = 0; enqueue(item: T): void { this.items.push(item); } dequeue(): T | undefined { if (this.head >= this.items.length) { return undefined; } return this.items[this.head++]; } get size(): number { return this.items.length - this.head; }} const history = new Stack<string>();history.push("home");history.push("search");history.push("results");console.log(history.pop(), history.peek(), history.size); const jobs = new Queue<number>();[10, 20, 30].forEach((job) => jobs.enqueue(job));console.log(jobs.dequeue(), jobs.dequeue(), jobs.size);console.log(jobs.dequeue(), jobs.dequeue());Follow the example
Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.
View the complete dry-run table
| Step / line | What happens | State after step | Output so far |
|---|---|---|---|
| T is filled in by the user: Stack<string> holds strings only. | — | — | |
| The return type forces callers to consider the empty stack. | — | — | |
| Dequeue reads at the head and moves it forward, in O(1). | — | — | |
| The last page visited is removed first; search is now on top. | — | results search 2 | |
| The oldest jobs come out first. | — | results search 2 10 20 1 | |
| The third call returns 30 and the fourth finds the queue empty. | — | results search 2 10 20 1 30 undefined |
Behind the scenes
How TypeScript processes this code
TypeScript checks types and emits JavaScript. Type annotations are removed before execution.
Cost and common mistakes
push, pop, peek, enqueue, and dequeue are all O(1).
- The queue's array keeps consumed items. A real implementation compacts it occasionally or uses a linked list.
- T | undefined makes an empty result visible, but a Stack<undefined> would be ambiguous. Avoid storing undefined.
- Generics are erased at run time. You cannot test x instanceof T.
Try it yourself
What does history.push(42) do?
Reveal answer and reasoning
It is a compile-time error, because history is a Stack<string> and 42 is a number.