What It Is
The Command pattern turns an action into a value: an object that knows how to execute itself — and often how to undo itself. Actions as values can be stored in arrays, queued, delayed, logged, and replayed.
The moment you need undo, you need commands: something must remember what was done, to do the opposite.
Example: Typing with Undo
Every word added to the document is a command with execute() and undo(). Executed commands go into a history array — undo is simply asking the last one to reverse itself.
Actions that can reverse themselves
The typo wrold is removed not by special code, but by the command that added it — it alone knows exactly what to take back. Each command holds the document array itself, so its push and pop change the caller's document.
Output: TypeScript file and captured output
interface Command {
execute(): void
undo(): void
}
class AddText implements Command {
private doc: string[]
private text: string
constructor(doc: string[], text: string) {
this.doc = doc
this.text = text
}
execute(): void {
this.doc.push(this.text)
}
undo(): void {
this.doc.pop()
}
}
const doc: string[] = []
const history: Command[] = []
for (const word of ["Hello", "wrold"]) {
const command = new AddText(doc, word)
command.execute()
history.push(command)
}
console.log("document: ", doc)
// The last command knows how to take itself back.
const last = history[history.length - 1]
last.undo()
console.log("after undo:", doc)
When to Use It
Use it for undo/redo, action queues, macros, and audit logs — anywhere actions must be stored or reversed.
Skip it when actions are fire-and-forget. A queued action with nothing to undo is often just a function value in an array — the full pattern earns its keep only when undo or inspection is needed.
Pros and Cons
Pros: undo/redo becomes natural; actions can be queued, logged and replayed; new actions plug in without touching the invoker.
Cons: one class per action adds up fast; correct undo state is subtle — a command must capture everything needed to reverse itself.