typescript 56 lines · 7 steps

Undo/redo with two stacks in TypeScript

A three-part state model — past, present, future — powers bounded undo and redo for an editor.

Explained by highlit
1type EditorState = {
2 content: string;
3 cursor: number;
4};
5 
6export class UndoRedoHistory {
7 private past: EditorState[] = [];
8 private future: EditorState[] = [];
9 private present: EditorState;
10 private readonly limit: number;
11 
12 constructor(initial: EditorState, limit = 100) {
13 this.present = initial;
14 this.limit = limit;
15 }
16 
17 get current(): EditorState {
18 return this.present;
19 }
20 
21 get canUndo(): boolean {
22 return this.past.length > 0;
23 }
24 
25 get canRedo(): boolean {
26 return this.future.length > 0;
27 }
28 
29 push(next: EditorState): void {
30 if (next.content === this.present.content && next.cursor === this.present.cursor) {
31 return;
32 }
33 this.past.push(this.present);
34 if (this.past.length > this.limit) {
35 this.past.shift();
36 }
37 this.present = next;
38 this.future = [];
39 }
40 
41 undo(): EditorState {
42 const previous = this.past.pop();
43 if (!previous) return this.present;
44 this.future.unshift(this.present);
45 this.present = previous;
46 return this.present;
47 }
48 
49 redo(): EditorState {
50 const next = this.future.shift();
51 if (!next) return this.present;
52 this.past.push(this.present);
53 this.present = next;
54 return this.present;
55 }
56}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Modeling history as past/present/future stacks makes undo and redo symmetric, mirror-image operations.
  2. 2Capping the past stack bounds memory so long editing sessions don't grow history without limit.
  3. 3Pushing a new state must clear the redo future, since diverging from history invalidates the old redo path.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Undo/redo with two stacks in TypeScript — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code