Implement the publish-subscribe pattern in TypeScript
Published Jan 16, 2023
⋅
Updated Dec 30, 2024
⋅
1 minutes read
Recently I was working on a refactor of a personal project and I needed to implement a publish-subscribe pattern. I had never done it before, so I decided to do some research and write a simple implementation in TypeScript.
What is the publish-subscribe pattern?
The pub-sub pattern is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.
Example
// subscribe.ts
export type ISubscribe<T> = {
getState: () => T;
setState: (value: Partial<T>) => void;
subscribe: (callback: () => void) => () => void;
};
export const subscription = <T>(initialValue: T): ISubscribe<T> => {
const listeners = new Set<() => void>();
let value = initialValue;
const getState = () => value;
const setState = (newValue: Partial<T>) => {
value = { ...value, ...newValue };
listeners.forEach((listener) => listener());
};
const subscribe = (callback: () => void) => {
listeners.add(callback);
// Return a function to unsubscribe.
return () => listeners.delete(callback);
};
return {
getState,
setState,
subscribe,
};
};
Testing the implementation
The test is using Vitest:
// subscribe.spec.ts
import { vi } from "vitest";
import { subscription } from ".";
describe("subscribe.ts", () => {
const baseStore = {
name: "foo",
surname: "bar",
};
it("should test the subscription implementation", () => {
const state = subscription(baseStore);
const sub = vi.fn();
expect(state.getState()).toEqual(baseStore);
state.subscribe(sub);
state.setState({ name: "baz" });
expect(sub).toHaveBeenCalledTimes(1);
expect(state.getState()).toEqual({ ...baseStore, name: "baz" });
});
});