TypeScript Programming Labs

Master-level TypeScript: decorators, mixins, and branded types for ultimate type safety.

Master Level - Module 8

Decorators, mixins, and branded/nominal types for enterprise-grade TypeScript.

Lab 22: TypeScript Decorators
Master
Coding Challenge
Your Task: Master TypeScript decorators - powerful metaprogramming tools for classes, methods, properties, and parameters.

Detailed Requirements:
1. Class decorator: Modify or replace class constructor:
function sealed(constructor: Function) { Object.seal(constructor); Object.seal(constructor.prototype); } @sealed class Greeter { greeting: string; constructor(message: string) { this.greeting = message; } }

2. Method decorator: Intercept method calls:
function log(target: any, key: string, descriptor: PropertyDescriptor) { const original = descriptor.value; descriptor.value = function(...args: any[]) { console.log(\`Calling \${key} with\`, args); const result = original.apply(this, args); console.log(\`Result:\`, result); return result; }; return descriptor; } class Calculator { @log add(a: number, b: number) { return a + b; } }

3. Property decorator: Observe property access:
function observable(target: any, key: string) { let value = target[key]; Object.defineProperty(target, key, { get: () => value, set: (newValue) => { console.log(\`Setting \${key} to \${newValue}\`); value = newValue; } }); }

4. Parameter decorator: Mark parameters for validation:
function required(target: any, key: string, index: number) { const requiredParams: number[] = Reflect.getMetadata("required", target, key) || []; requiredParams.push(index); Reflect.defineMetadata("required", requiredParams, target, key); }

5. Decorator factory: Create configurable decorators:
function minLength(min: number) { return function(target: any, key: string) { let value: string; Object.defineProperty(target, key, { get: () => value, set: (newValue: string) => { if (newValue.length < min) { throw new Error(\`Min length is \${min}\`); } value = newValue; } }); }; }

Expected Output:
Class decorator: sealed Method decorator: logging calls Property decorator: observable Decorator factory: minLength(3) Decorators complete!

Requirements Checklist

Create a class decorator
Create a method decorator
Create a property decorator
Create a decorator factory
Use PropertyDescriptor
Apply decorators with @ syntax
Output
// Click "Run Code" to compile and execute
Hints & Tips
� Class: function dec(constructor: Function) { }
� Method: function dec(target, key, descriptor) { }
� Property: function dec(target, key) { }
� Factory: function dec(arg) { return function(target) { } }
� Apply: @decorator above class/method/property
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 23: TypeScript Mixins
Master
Coding Challenge
Your Task: Master TypeScript mixins - a pattern for composing classes from reusable components without inheritance chains.

Detailed Requirements:
1. Basic mixin pattern:
type Constructor = new (...args: any[]) => T; function Timestamped(Base: TBase) { return class extends Base { timestamp = Date.now(); }; }

2. Mixin with methods:
function Activatable(Base: TBase) { return class extends Base { isActive = false; activate() { this.isActive = true; } deactivate() { this.isActive = false; } }; }

3. Constrained mixin: Require base class features:
interface Nameable { name: string; } function Tagged>(Base: TBase) { return class extends Base { tag = \`[\${this.name}]\`; }; }

4. Compose multiple mixins:
class User { name: string; constructor(name: string) { this.name = name; } } const TimestampedActivatableUser = Timestamped(Activatable(User)); const user = new TimestampedActivatableUser("Alice"); user.activate(); console.log(user.timestamp, user.isActive);

5. Generic mixin with interface:
interface Disposable { dispose(): void; isDisposed: boolean; } function DisposableMixin(Base: TBase) { return class extends Base implements Disposable { isDisposed = false; dispose() { this.isDisposed = true; console.log("Disposed"); } }; }

Expected Output:
Timestamped mixin: added timestamp Activatable mixin: activate/deactivate Composed: TimestampedActivatableUser Constrained mixin: requires Nameable Mixins complete!

Requirements Checklist

Define Constructor type
Create a basic mixin function
Create mixin with methods
Create constrained mixin
Compose multiple mixins
Test composed class
Output
// Click "Run Code" to compile and execute
Hints & Tips
� Constructor type: type Constructor<T = {}> = new (...args: any[]) => T
� Mixin: function Mix<T extends Constructor>(Base: T) { return class extends Base { } }
� Constrain: T extends Constructor<RequiredInterface>
� Compose: Mixin1(Mixin2(BaseClass))
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

Lab 24: Branded & Nominal Types
Master
Coding Challenge
Your Task: Master branded/nominal types to create distinct types that are structurally identical but semantically different, preventing accidental misuse.

Detailed Requirements:
1. Basic branded type:
type Brand = K & { __brand: T }; type USD = Brand; type EUR = Brand; const usd = 100 as USD; const eur = 85 as EUR; function addUSD(a: USD, b: USD): USD { return (a + b) as USD; } // addUSD(usd, eur); // Error! Can't mix currencies

2. Branded string types:
type Email = Brand; type URL = Brand; type UUID = Brand; function validateEmail(input: string): Email | null { const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; return regex.test(input) ? input as Email : null; } function sendEmail(to: Email, subject: string) { console.log(\`Sending to \${to}\`); }

3. ID types for different entities:
type UserId = Brand; type PostId = Brand; type CommentId = Brand; function getUser(id: UserId) { ... } function getPost(id: PostId) { ... } const userId = "user-123" as UserId; const postId = "post-456" as PostId; getUser(userId); // OK // getUser(postId); // Error! Type mismatch

4. Validated number ranges:
type Percentage = Brand; type PositiveInt = Brand; function toPercentage(n: number): Percentage | null { return n >= 0 && n <= 100 ? n as Percentage : null; } function toPositiveInt(n: number): PositiveInt | null { return Number.isInteger(n) && n > 0 ? n as PositiveInt : null; }

5. Opaque types with constructors:
// More robust pattern with private constructor declare const validatedSymbol: unique symbol; type Validated = T & { [validatedSymbol]: true }; type ValidatedUser = Validated<{ name: string; email: string; }>; function validateUser(input: unknown): ValidatedUser | null { // validation logic if (isValidUser(input)) { return input as ValidatedUser; } return null; }

Expected Output:
USD branded: cannot mix with EUR Email branded: validated string UserId/PostId: distinct entity IDs Percentage: validated 0-100 range Nominal types prevent misuse!

Requirements Checklist

Create Brand utility type
Create currency branded types
Create string branded types (Email, URL)
Create entity ID types
Create validation functions
Test type safety
Output
// Click "Run Code" to compile and execute
Hints & Tips
� Brand: type Brand<K, T> = K & { __brand: T }
� Create: type USD = Brand<number, "USD">
� Cast: const usd = 100 as USD
� Validate: Return BrandedType | null
� Prevents: Mixing USD with EUR, UserId with PostId
Progress: 0/6
Score: 0/100
0%

Lab Results

Review feedback below

TypeScript Master Level Complete!

Incredible achievement! You've mastered decorators, mixins, and branded types. You're now a TypeScript master ready for any enterprise challenge!