TypeScript · Course navigation

TypeScript generic stack and queue

Write containers that work for any element type and keep it checked.

On this page
Before you start

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

TypeScript
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());
Output
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
Guided walkthroughReady
1class Stack<T> {
2 private items: T[] = [];
3
4 push(item: T): void {
5 this.items.push(item);
6 }
7
8 pop(): T | undefined {
9 return this.items.pop();
10 }
11
12 peek(): T | undefined {
13 return this.items[this.items.length - 1];
14 }
15
16 get size(): number {
17 return this.items.length;
18 }
19}
20
21class Queue<T> {
22 private items: T[] = [];
23 private head = 0;
24
25 enqueue(item: T): void {
26 this.items.push(item);
27 }
28
29 dequeue(): T | undefined {
30 if (this.head >= this.items.length) {
31 return undefined;
32 }
33 return this.items[this.head++];
34 }
35
36 get size(): number {
37 return this.items.length - this.head;
38 }
39}
40
41const history = new Stack<string>();
42history.push("home");
43history.push("search");
44history.push("results");
45console.log(history.pop(), history.peek(), history.size);
46
47const jobs = new Queue<number>();
48[10, 20, 30].forEach((job) => jobs.enqueue(job));
49console.log(jobs.dequeue(), jobs.dequeue(), jobs.size);
50console.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
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput 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.

Further reading