How to implement a command pattern in Typescript

The command pattern is used to separate each request or command from the object on which those requests are performed. This allows a request to be parameterized for different scenarios and moves the code into different classes for the execution of requests.

Why use the command pattern?

By using the command pattern in our programs and applications, we are able to incorporate functionalities (such as queueing, request logging, and undo/redo operations) with a lot of ease. This is because each request is an independent entity from other classes and objects; therefore, we can modify or update a request without worrying about affecting other requests or objects.

Design principles utilized by the command pattern

Separation of concerns: The remote will know how to make a request but is not concerned with what happens or how the request is executed.

Programming to an Interface : Because each request is an implementation of the same interface, it allows new command objects to be created independently without altering any previously written code.

Low coupling: The command objects only interact with other classes when a request is made for a particular action without sharing any other information. Due to this, changes in one class’s implementation won’t affect how the other class communicates with it.

Class diagram depicting a generic command pattern implementation.

Example

Below is an example code of the command pattern. The Television class is a simple object and the Remote class acts as the caller, using the TelevisionOnCommand and TelevisionOffCommand classes to interact with the Television objects.

The TelevisionOnCommand and TelevisionOffCommand classes are concrete implementations of the Command interface.

// the television class
export class Television {
state: boolean = false;
on() {
this.state = true;
}
off() {
this.state = false;
}
}
// the command interface
interface Command {
execute(): any;
undo(): any;
}
// the televisiononcommand is a concrete implementation of the command interface
class TelevisionOnCommand implements Command {
television: Television;
constructor(television: Television) {
this.television = television;
}
execute() {
this.television.on();
}
undo() {
this.television.off();
}
}
// the televisionoffcommand is a concrete implementation of the command interface
class TelevisionOffCommand implements Command {
television: Television;
constructor(television: Television) {
this.television = television;
}
execute() {
this.television.off();
}
undo() {
this.television.on();
}
}
// the remote in this case is the caller
class Remote {
onCommand: Command;
offCommand: Command;
setCommand(onCommand, offCommand) {
this.onCommand = onCommand;
this.offCommand = offCommand;
}
onButtonClick() {
this.onCommand.execute();
}
offButtonClick() {
this.offCommand.execute()
}
}
let television = new Television();
let televisionOnCommand = new TelevisionOnCommand(television);
let televisionOffCommand = new TelevisionOffCommand(television);
let remote = new Remote();
remote.setCommand(televisionOnCommand, televisionOffCommand);
console.log('state of television before remote is used:', television.state);
remote.onButtonClick();
console.log('state of television after remote is used:', television.state);

Free Resources