# A Stack data structure in TypeScript

**Date:** 2020-12-05  
**Author:** Kees C. Bakker  
**Categories:** TypeScript  
**Original:** https://keestalkstech.com/a-stack-data-structure-in-typescript/

![Stack of plates.](https://keestalkstech.com/wp-content/uploads/2020/12/brooke-lark-sG-PR0BNwb4-unsplash-scaled.jpg)

---

The JavaScript `Array` has everything to acts as a nice `Stack` structure, the only thing is missing is a `peek()` method. In this blog I'll discuss two ways of implementing a stack based on the array.

[outline]

## What's a stack?

A picture says more than words:

![This image shows how pop and push works on a stack data structure.](https://keestalkstech.com/wp-content/uploads/2020/12/stack.png)
*A stack is a LIFO structure: last in, first out. Imagine a stack of plates. Source: Stack (abstract data type)*

Let's start with defining the what a stack should look like and let's keep it as close to what the `Array` already gives us:

```typescript
interface Stack<T> {
  push(item: T): number
  pop(): T | undefined
  peek(): T | undefined
  readonly length: number
  [Symbol.iterator](): Iterator<T>
}
```

*Note: I've included the iterator to support [spreading](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax) without having to cast.*

## Module augmentation

The `push`, `pop`, `length` and `iterator` are already present on the `Array` object. The only thing we miss is the `peek` object. Let's use [TypeScript module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) to add the `peek` feature to the array:

```typescript
//!file: m-stack.ts

export interface Stack<T> {
  push(item: T): number
  pop(): T | undefined
  peek(): T | undefined
  readonly length: number
  [Symbol.iterator](): Iterator<T>
}

declare global {
  interface Array<T> {
    peek(): T
  }
}

if (!Array.prototype.peek) {
  Array.prototype.peek = function <T>(): T {
    return this.length == 0
      ? undefined
      : this[this.length - 1]
  }
}
```

You can use it like this:

```typescript
import { Stack } from "./m-stack"
import "./m-stack"

let stack: Stack<string> = []
stack.push("kaas")
stack.push("is", "heel")
stack.push("lekker")

console.log(stack.length, stack.peek());
stack.pop()
console.log(stack.length, stack.peek());
```

When we forget `import "./m-stack"`, we'll get a runtime error: `TypeError: stack.peek is not a function`! Took me some time to figure out why.

Pro:

Con:

## A class wrapper

If we don't want to pollute the array, we could create our own `Stack` class based on the `Array`:

```typescript
//!file: n-stack.ts

export class Stack<T> {
  private items: T[]

  constructor(iterable: Iterable<T> = []) {
    this.items = [...iterable]
  }

  pop = () => this.items.pop()
  push = (...items: T[]) => this.items.push(...items)
  peek = () =>
    this.length == 0
      ? undefined
      : this.items[this.length - 1]

  get length() {
    return this.items.length
  }

  *[Symbol.iterator]() {
    for (let item of this.items) yield item
  }
}
```

Usage is similar:

```typescript
import { Stack } from "./n-stack"

let stack = new Stack<string>()
stack.push("kaas")
stack.push("is", "heel")
stack.push("lekker")

console.log(stack.length, stack.peek());
stack.pop()
console.log(stack.length, stack.peek());

console.log([...stack])
```

Pro:

Con:

## Bonus: Queue

If you want a queue in the same way, just switch the `pop` for `shift`. The `peek` is always the first items of the array.

```typescript
export class Queue<T> {
  private items: T[]

  constructor(iterable: Iterable<T> = []) {
    this.items = [...iterable]
  }

  shift = () => this.items.shift()
  push = (...items: T[]) => this.items.push(...items)
  peek = () =>
    this.length == 0
      ? undefined
      : this.items[0]

  get length() {
    return this.items.length
  }

  *[Symbol.iterator]() {
    for (let item of this.items) yield item
  }
}
```

## Conclusion

Implementing a Queue or a Stack in TypeScript is pretty straightforward. The heavy lifting has already been done by the native `Array` class. My preference goes to the class-implementation as it does not pollute and is easy to use. I would advise to stay as close to the `Array` API as possible.
