TypeScript Programming Labs

Architect-level TypeScript: design patterns, dependency injection, and real-world architecture.

Architect Level - Module 10

Design patterns, dependency injection, and enterprise architecture patterns.

Lab 28: Design Patterns
Architect
Coding Challenge
Your Task: Implement classic design patterns with full TypeScript type safety.

Detailed Requirements:
1. Singleton pattern:
class Database { private static instance: Database; private constructor() {} static getInstance(): Database { if (!Database.instance) { Database.instance = new Database(); } return Database.instance; } query(sql: string): void { console.log(\`Executing: \${sql}\`); } }

2. Factory pattern:
interface Product { name: string; price: number; } interface ProductFactory { create(name: string, price: number): Product; } class DigitalProductFactory implements ProductFactory { create(name: string, price: number): Product { return { name, price, type: "digital" }; } } class PhysicalProductFactory implements ProductFactory { create(name: string, price: number): Product { return { name, price, type: "physical", weight: 0 }; } }

3. Observer pattern:
interface Observer<T> { update(data: T): void; } class Subject<T> { private observers: Observer<T>[] = []; subscribe(observer: Observer<T>): void { this.observers.push(observer); } unsubscribe(observer: Observer<T>): void { this.observers = this.observers.filter(o => o !== observer); } notify(data: T): void { this.observers.forEach(o => o.update(data)); } }

4. Strategy pattern:
interface PaymentStrategy { pay(amount: number): void; } class CreditCardPayment implements PaymentStrategy { constructor(private cardNumber: string) {} pay(amount: number) { console.log(\`Paid \${amount} with card \${this.cardNumber}\`); } } class PayPalPayment implements PaymentStrategy { constructor(private email: string) {} pay(amount: number) { console.log(\`Paid \${amount} via PayPal: \${this.email}\`); } } class PaymentProcessor { constructor(private strategy: PaymentStrategy) {} setStrategy(strategy: PaymentStrategy) { this.strategy = strategy; } process(amount: number) { this.strategy.pay(amount); } }

Expected Output:
Singleton: single Database instance Factory: created Digital/Physical products Observer: notified 3 subscribers Strategy: CreditCard/PayPal payments Design patterns complete!

Requirements Checklist

Implement Singleton pattern
Implement Factory pattern
Implement Observer pattern
Implement Strategy pattern
Use interfaces for abstraction
Test all patterns
Output
// Click "Run Code" to compile and execute
Hints & Tips
� Singleton: private constructor + static getInstance()
� Factory: interface with create() method
� Observer: subscribe/unsubscribe/notify methods
� Strategy: interchangeable algorithms via interface
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 29: Dependency Injection
Architect
Coding Challenge
Your Task: Implement a dependency injection container with TypeScript for loosely coupled, testable code.

Detailed Requirements:
1. Define service interfaces:
interface ILogger { log(message: string): void; error(message: string): void; } interface IUserRepository { findById(id: string): Promise<User | null>; save(user: User): Promise<void>; } interface IEmailService { send(to: string, subject: string, body: string): Promise<void>; }

2. Create service implementations:
class ConsoleLogger implements ILogger { log(message: string) { console.log(\`[LOG] \${message}\`); } error(message: string) { console.error(\`[ERROR] \${message}\`); } } class UserRepository implements IUserRepository { constructor(private logger: ILogger) {} async findById(id: string): Promise<User | null> { this.logger.log(\`Finding user: \${id}\`); return { id, name: "John" }; } async save(user: User): Promise<void> { this.logger.log(\`Saving user: \${user.id}\`); } }

3. Build a DI container:
type Constructor<T> = new (...args: any[]) => T; class Container { private services = new Map<string, any>(); register<T>(token: string, instance: T): void { this.services.set(token, instance); } resolve<T>(token: string): T { const service = this.services.get(token); if (!service) throw new Error(\`Service not found: \${token}\`); return service; } }

4. Decorator-based injection:
const Injectable = (): ClassDecorator => { return (target) => { Reflect.defineMetadata("injectable", true, target); }; }; const Inject = (token: string): ParameterDecorator => { return (target, key, index) => { const existing = Reflect.getMetadata("inject", target) || []; existing.push({ index, token }); Reflect.defineMetadata("inject", existing, target); }; };

5. Use the container:
const container = new Container(); // Register services container.register("ILogger", new ConsoleLogger()); container.register("IUserRepository", new UserRepository(container.resolve("ILogger")) ); // Resolve and use const userRepo = container.resolve<IUserRepository>("IUserRepository"); await userRepo.findById("123");

Expected Output:
Container: registered 3 services Resolved: ILogger, IUserRepository, IEmailService Injection: constructor injection working DI container complete!

Requirements Checklist

Define service interfaces
Create service implementations
Build Container class with register/resolve
Implement constructor injection
Use interfaces for dependencies
Test the DI container
Output
// Click "Run Code" to compile and execute
Hints & Tips
� Interface: interface IService { method(): void; }
� Container: Use Map<string, any> for services
� Register: services.set(token, instance)
� Resolve: services.get(token) as T
� Injection: Pass resolved deps to constructor
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 30: Real-World Architecture
Architect
Coding Challenge
Your Task: Build a complete layered architecture with repository pattern, service layer, and DTOs.

Detailed Requirements:
1. Entity and DTO types:
// Entity (database model) interface UserEntity { id: string; email: string; passwordHash: string; createdAt: Date; updatedAt: Date; } // DTO (Data Transfer Object) interface CreateUserDTO { email: string; password: string; } interface UserResponseDTO { id: string; email: string; createdAt: string; }

2. Repository pattern:
interface Repository<T, ID> { findById(id: ID): Promise<T | null>; findAll(): Promise<T[]>; create(entity: Omit<T, "id">): Promise<T>; update(id: ID, entity: Partial<T>): Promise<T>; delete(id: ID): Promise<void>; } class UserRepository implements Repository<UserEntity, string> { private users: Map<string, UserEntity> = new Map(); async findById(id: string): Promise<UserEntity | null> { return this.users.get(id) ?? null; } // ... other methods }

3. Service layer:
class UserService { constructor( private userRepo: Repository<UserEntity, string>, private emailService: IEmailService, private logger: ILogger ) {} async createUser(dto: CreateUserDTO): Promise<UserResponseDTO> { this.logger.log(\`Creating user: \${dto.email}\`); const entity = await this.userRepo.create({ email: dto.email, passwordHash: await this.hashPassword(dto.password), createdAt: new Date(), updatedAt: new Date() }); await this.emailService.send(dto.email, "Welcome!", "..."); return this.toDTO(entity); } private toDTO(entity: UserEntity): UserResponseDTO { return { id: entity.id, email: entity.email, createdAt: entity.createdAt.toISOString() }; } }

4. Controller/Handler layer:
class UserController { constructor(private userService: UserService) {} async handleCreateUser(req: Request): Promise<Response> { try { const dto: CreateUserDTO = req.body; const user = await this.userService.createUser(dto); return { status: 201, body: user }; } catch (error) { return { status: 400, body: { error: error.message } }; } } }

Expected Output:
Entity/DTO: defined and mapped Repository: CRUD operations Service: business logic layer Controller: HTTP handlers Architecture complete!

Requirements Checklist

Define Entity and DTO types
Implement Repository interface
Create Service layer with business logic
Implement Controller/Handler layer
Add DTO mapping (toDTO function)
Test the full architecture
Output
// Click "Run Code" to compile and execute
Hints & Tips
� Entity: Full database model with all fields
� DTO: Only fields needed for API input/output
� Repository: Generic interface with CRUD methods
� Service: Contains business logic, uses repository
� Controller: Handles HTTP, calls service methods
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

TypeScript Certification Complete!

Congratulations! You've completed all 30 TypeScript labs covering beginner to architect-level concepts. You are now a certified TypeScript expert ready for any enterprise project!