TypeScript State Design Pattern

Ibrahim sengun
2 min readJan 28, 2024

--

What is state design pattern?

The state design pattern is a behavioral design pattern that provides the ability to alter the internal state of objects in case of changes, as if the object changed its class.

There are several terminologies in the state design pattern. These are:

  • Context: It stores references to the concrete state objects and communicates with state objects via the interface.
  • State: It’s the interface that defines the state-specific properties.
  • Concrete State: It implements the state and handles the properties defined in the state.
  • Client: This refers to the application or function that communicates with the system.

When should the state design pattern be used?

The state design pattern can be used when objects behave differently based on their current state, or when there is an object that consists of multiple conditional rules.

How to implement state design pattern in TypeScript

Let’s apply the state design pattern to TypeScript. First, let’s imagine a scenario where we are designing a mechanism for write and read permissions for users. Initially, we decide to take the easy way and create a large conditional cloud with “if” statements. This approach worked; however, it also made our code ugly and complicated. As the number of conditional states increased, these problems also escalated. To address these issues, we decided to employ the state design pattern.

Using this pattern, we create objects for each conditional state and connect them with a higher-level interface to avoid code duplication. Subsequently, we create a controller context that determines which state to use and handles state changes.

State design pattern diagram

State design pattern diagram

State design pattern code

// State Interface
interface IUserState {
viewDashboard(): void;
editContent(): void;
}

// Concrete State
class AdminState implements IUserState {
viewDashboard(): void {
console.log("Admin viewing dashboard");
}

editContent(): void {
console.log("Admin editing content");
}
}

// Concrete State
class BasicUserState implements IUserState {
viewDashboard(): void {
console.log("Basic User viewing dashboard");
}

editContent(): void {
console.log("Basic User does not have permission to edit content");
}
}

// Context
class UserContext {
private currentState: IUserState;

constructor(initialState: IUserState) {
this.currentState = initialState;
}

changeState(newState: IUserState): void {
this.currentState = newState;
}

viewDashboard(): void {
this.currentState.viewDashboard();
}

editContent(): void {
this.currentState.editContent();
}
}

// Client
const adminState = new AdminState();
const basicUserState = new BasicUserState();

const userContext = new UserContext(adminState);

// Output: Admin viewing dashboard
userContext.viewDashboard();
// Output: Admin editing content
userContext.editContent();

userContext.changeState(basicUserState);

// Output: Basic User viewing dashboard
userContext.viewDashboard();
// Output: Basic User does not have permission to edit content
userContext.editContent();

--

--