Composite Pattern - Tree Structures in TypeScript

Learn the Composite pattern for building tree structures. TypeScript examples for file systems, UI component trees, organization charts, and menu hierarchies.

Structural

Detailed Explanation

Composite Pattern

The Composite pattern composes objects into tree structures to represent part-whole hierarchies. Clients can treat individual objects (leaves) and compositions (branches) uniformly through a common interface.

Why Uniform Treatment Matters

Without Composite, client code must distinguish between leaf and container objects:

// Without Composite: messy type checking
function calculatePrice(item: Product | ProductBundle) {
  if (item instanceof ProductBundle) {
    return item.items.reduce((sum, i) => sum + calculatePrice(i), 0);
  }
  return item.price;
}

With Composite, both implement the same interface:

interface Priceable {
  getPrice(): number;
}

class Product implements Priceable {
  constructor(public name: string, public price: number) {}
  getPrice() { return this.price; }
}

class Bundle implements Priceable {
  private items: Priceable[] = [];
  add(item: Priceable) { this.items.push(item); }
  getPrice() { return this.items.reduce((s, i) => s + i.getPrice(), 0); }
}

Recursive Operations

The beauty of Composite is that operations naturally recurse through the tree. Calling getPrice() on a bundle automatically sums the prices of all nested products and sub-bundles, regardless of depth.

Real-World Trees

  • DOM: Every HTML element can contain child elements. Operations like querySelectorAll traverse the composite tree.
  • React component tree: Components contain other components, forming a composite hierarchy.
  • File systems: Directories contain files and other directories.
  • Org charts: Departments contain teams, which contain employees.

Design Consideration

The main tension in Composite is safety vs transparency. Should leaf nodes have add()/remove() methods that throw errors? Or should these methods only exist on composite nodes, requiring type checks? Modern TypeScript favors separate interfaces for leaves and composites with a shared base.

Use Case

Composite is perfect for file system browsers (files and folders), UI component hierarchies (React component trees), e-commerce product bundles with nested sub-bundles, organization charts, and any domain with recursive part-whole relationships.

Try It — Design Pattern Reference

Open full tool