Why Types Matter for Design Patterns
Design patterns are proven solutions to common software design problems. TypeScript's type system makes implementing these patterns safer, more expressive, and self-documenting.
The Problem with Untyped Patterns
Consider a simple Factory pattern in JavaScript:
javascript1// JavaScript - No type safety2function createShippingLabel(carrier) {3 switch (carrier) {4 case 'usps':5 return { format: 'USPS', barcode: '...' };6 case 'fedex':7 return { format: 'FedEx', tracking: '...' };8 // What if someone passes 'ups' but we forgot to handle it?9 }10}
Problems:
- No way to know what
carriervalues are valid - Return type is unclear
- Missing cases aren't caught until runtime
- Refactoring is risky
TypeScript to the Rescue
typescript1// TypeScript - Type-safe2type Carrier = 'usps' | 'fedex' | 'ups';34interface ShippingLabel {5 carrier: Carrier;6 format: string;7 tracking: string;8}910function createShippingLabel(carrier: Carrier): ShippingLabel {11 switch (carrier) {12 case 'usps':13 return { carrier, format: 'USPS Priority', tracking: '9400...' };14 case 'fedex':15 return { carrier, format: 'FedEx Ground', tracking: '7489...' };16 case 'ups':17 return { carrier, format: 'UPS Ground', tracking: '1Z...' };18 }19}
Benefits:
Carriertype documents valid values- Return type is explicit
- TypeScript warns if we miss a case
- IDE provides autocomplete and refactoring support
Types Enable Pattern Contracts
Design patterns define contracts between components:
| Pattern | Contract |
|---|---|
| Factory | "I create objects of type X" |
| Strategy | "I implement algorithm interface Y" |
| Observer | "I notify subscribers with event Z" |
TypeScript interfaces make these contracts explicit and enforceable:
typescript1// Strategy pattern contract2interface ShippingRateStrategy {3 calculate(package: Package): Money;4}56// Implementations must follow the contract7class WeightBasedStrategy implements ShippingRateStrategy {8 calculate(pkg: Package): Money {9 return { amount: pkg.weight * 5.00, currency: 'USD' };10 }11}
Key TypeScript Features for Patterns
Throughout this course, you'll use these TypeScript features:
- Interfaces - Define contracts for pattern participants
- Type Aliases - Create readable, reusable type definitions
- Generics - Build flexible, type-safe abstractions
- Union Types - Model finite sets of options
- Access Modifiers - Enforce encapsulation
- Abstract Classes - Share implementation in class hierarchies
Logistics Domain Preview
We'll apply patterns to real logistics scenarios:
typescript1// Address types we'll build2interface Address {3 street: string;4 city: string;5 state: string;6 postalCode: string;7 country: Country;8}910type Country = 'US' | 'UK' | 'DE' | 'FR' | 'CA';1112// Package tracking we'll implement13interface TrackingEvent {14 timestamp: Date;15 status: PackageStatus;16 location: string;17}1819type PackageStatus =20 | 'created'21 | 'picked_up'22 | 'in_transit'23 | 'out_for_delivery'24 | 'delivered';
Summary
TypeScript transforms design patterns from informal conventions into enforced contracts:
- Compile-time safety catches errors before runtime
- Self-documenting code through explicit types
- Refactoring confidence with IDE support
- Pattern enforcement through interfaces and abstract classes
Next, let's dive into interfaces and type aliases - the foundation of typed patterns.