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.
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
querySelectorAlltraverse 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.