15 minlesson

Why Types Matter for Design Patterns

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:

javascript
1// JavaScript - No type safety
2function 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 carrier values are valid
  • Return type is unclear
  • Missing cases aren't caught until runtime
  • Refactoring is risky

TypeScript to the Rescue

typescript
1// TypeScript - Type-safe
2type Carrier = 'usps' | 'fedex' | 'ups';
3
4interface ShippingLabel {
5 carrier: Carrier;
6 format: string;
7 tracking: string;
8}
9
10function 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:

  • Carrier type 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:

PatternContract
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:

typescript
1// Strategy pattern contract
2interface ShippingRateStrategy {
3 calculate(package: Package): Money;
4}
5
6// Implementations must follow the contract
7class 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:

  1. Interfaces - Define contracts for pattern participants
  2. Type Aliases - Create readable, reusable type definitions
  3. Generics - Build flexible, type-safe abstractions
  4. Union Types - Model finite sets of options
  5. Access Modifiers - Enforce encapsulation
  6. Abstract Classes - Share implementation in class hierarchies

Logistics Domain Preview

We'll apply patterns to real logistics scenarios:

typescript
1// Address types we'll build
2interface Address {
3 street: string;
4 city: string;
5 state: string;
6 postalCode: string;
7 country: Country;
8}
9
10type Country = 'US' | 'UK' | 'DE' | 'FR' | 'CA';
11
12// Package tracking we'll implement
13interface TrackingEvent {
14 timestamp: Date;
15 status: PackageStatus;
16 location: string;
17}
18
19type 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.