← Back to TypeScript Course | Chapter 16: Advanced Patterns | Lesson 4 of 7

Observer Pattern

The Observer Pattern lets one object notify multiple dependent objects when its state changes. It is useful for event systems, application state updates, and one-to-many communication.
Syntax
typescript
interface Observer<T> {
  update(data: T): void;
}
class Subject<T> {
  private observers: Observer<T>[] = [];
  subscribe(o: Observer<T>) { this.observers.push(o); }
  notify(data: T) { this.observers.forEach(o => o.update(data)); }
}

Basic Observer

A basic observer setup has a subject that maintains a list of typed callback functions and calls each of them whenever some event or state change occurs, decoupling the thing that changes from the things reacting to it.

Example: Basic Observer

typescript
type Callback = (data: string) => void;
class Subject {
  private observers: Callback[] = [];
  subscribe(cb: Callback) { this.observers.push(cb); }
  notify(data: string) { this.observers.forEach((cb) => cb(data)); }
}
const subject = new Subject();
subject.subscribe((data) => console.log("Received:", data));
subject.notify("event fired");

Subscribe and Unsubscribe

Subscribing adds a typed callback to the subject's internal list, while unsubscribing removes it — typically by returning a cleanup function from subscribe itself so callers don't need to track the callback reference separately.

Example: Subscribe and Unsubscribe

typescript
type Callback = () => void;
class Subject {
  private observers: Callback[] = [];
  subscribe(cb: Callback): () => void {
    this.observers.push(cb);
    return () => {
      this.observers = this.observers.filter((o) => o !== cb);
    };
  }
}
const subject = new Subject();
const unsubscribe = subject.subscribe(() => console.log("notified"));
unsubscribe();
console.log("Unsubscribed");

Observers and State

Observers reacting to state changes typically receive the new state as a typed argument when notified, letting each observer update itself based on exactly what changed rather than re-fetching state independently.

Example: Observers and State

typescript
interface State { count: number; }
type Callback = (state: State) => void;
class Store {
  private observers: Callback[] = [];
  subscribe(cb: Callback) { this.observers.push(cb); }
  setState(state: State) { this.observers.forEach((cb) => cb(state)); }
}
const store = new Store();
store.subscribe((state) => console.log("New count:", state.count));
store.setState({ count: 5 });

Typed Event Objects

Typing the event object passed to observers — rather than passing loose, individual arguments — makes it easy to add new fields to what observers receive later without breaking every existing observer's function signature.

Example: Typed Event Objects

typescript
interface ClickEvent {
  x: number;
  y: number;
}
type Callback = (event: ClickEvent) => void;
class Subject {
  private observers: Callback[] = [];
  subscribe(cb: Callback) { this.observers.push(cb); }
  notify(event: ClickEvent) { this.observers.forEach((cb) => cb(event)); }
}
const subject = new Subject();
subject.subscribe((e) => console.log(e.x, e.y));
subject.notify({ x: 10, y: 20 });

When to Use Observer

Reach for the observer pattern when multiple independent parts of a program need to react to the same event source, like UI components all needing to update when shared data changes — for a single consumer, a plain callback is simpler.

Example: When to Use Observer

typescript
type Callback = (value: number) => void;
class DataSource {
  private observers: Callback[] = [];
  subscribe(cb: Callback) { this.observers.push(cb); }
  update(value: number) { this.observers.forEach((cb) => cb(value)); }
}
const source = new DataSource();
source.subscribe((v) => console.log("Widget A:", v));
source.subscribe((v) => console.log("Widget B:", v));
source.update(42);
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Forgetting to remove observers, which keeps them in the list and causes memory leaks.
  2. Mutating the observers array while looping over it during notify, so some callbacks are skipped.
  3. Calling notify with data that does not match the callback's parameter type.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.