+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 39 of 355

๐ŸŽฏ Interfaces in TypeScript: Defining Object Shapes

Master TypeScript interfaces to create powerful contracts and define the shape of your objects with precision ๐Ÿš€

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Basic TypeScript types knowledge ๐Ÿ“
  • Understanding of objects โšก
  • Function type basics ๐Ÿ’ป

What you'll learn

  • Understand interface fundamentals ๐ŸŽฏ
  • Create and implement interfaces effectively ๐Ÿ—๏ธ
  • Use optional and readonly properties ๐Ÿ›ก๏ธ
  • Master interface extension and composition โœจ

๐ŸŽฏ Introduction

Welcome to the world of TypeScript interfaces! ๐ŸŽ‰ In this guide, weโ€™ll explore one of TypeScriptโ€™s most powerful features for creating contracts that define the shape of objects, ensuring type safety and code clarity.

Youโ€™ll discover how interfaces are like blueprints ๐Ÿ“ - they describe what an object should look like without implementing the details. Whether youโ€™re defining API responses ๐ŸŒ, component props ๐ŸŽจ, or complex data structures ๐Ÿ—๏ธ, understanding interfaces is essential for writing maintainable, type-safe TypeScript code.

By the end of this tutorial, youโ€™ll be confidently designing interfaces that make your code more predictable and easier to work with! Letโ€™s dive in! ๐ŸŠโ€โ™‚๏ธ

๐Ÿ“š Understanding Interfaces

๐Ÿค” What are Interfaces?

Interfaces are like contracts or agreements ๐Ÿ“œ. Think of them as a promise that says โ€œany object of this type will have these specific properties and methods.โ€

In TypeScript terms, interfaces:

  • โœจ Define the structure of objects
  • ๐Ÿš€ Create reusable type definitions
  • ๐Ÿ›ก๏ธ Enforce type checking at compile time
  • ๐Ÿ”ง Support optional and readonly properties

๐Ÿ’ก Why Use Interfaces?

Hereโ€™s why developers love interfaces:

  1. Type Safety ๐Ÿ”’: Catch errors before runtime
  2. Code Documentation ๐Ÿ“–: Interfaces describe your data
  3. IntelliSense Support ๐Ÿ’ป: Better autocomplete in IDEs
  4. Flexibility ๐Ÿคธโ€โ™‚๏ธ: Extend and compose interfaces easily

Real-world example: Imagine building an e-commerce site ๐Ÿ›’. An interface for Product ensures every product has required fields like name, price, and id, preventing bugs from missing data.

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Simple Interface Example

Letโ€™s start with a friendly example:

// ๐Ÿ‘ค Define a User interface
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
  isActive: boolean;
}

// โœ… Create objects that match the interface
const user1: User = {
  id: 1,
  name: 'Alice Johnson',
  email: '[email protected]',
  age: 28,
  isActive: true
};

// โŒ This would error - missing required properties
// const user2: User = {
//   id: 2,
//   name: 'Bob Smith'
//   // Missing email, age, and isActive!
// };

// ๐ŸŽฏ Function using the interface
function greetUser(user: User): string {
  return `Hello, ${user.name}! ๐Ÿ‘‹ Your email is ${user.email}`;
}

console.log(greetUser(user1));

// ๐Ÿ’ก TypeScript ensures type safety
function updateUserAge(user: User, newAge: number): User {
  return {
    ...user,
    age: newAge
  };
}

const updatedUser = updateUserAge(user1, 29);
console.log(`${updatedUser.name} is now ${updatedUser.age} years old! ๐ŸŽ‚`);

๐Ÿ’ก Explanation: The User interface defines exactly what properties a user object must have, ensuring consistency across your application!

๐ŸŽฏ Optional and Readonly Properties

Not all properties are required or should be modifiable:

// ๐Ÿ  Address interface with optional and readonly properties
interface Address {
  readonly id: string;        // ๐Ÿ”’ Can't be changed after creation
  street: string;
  city: string;
  state: string;
  zipCode: string;
  country?: string;          // ? Makes it optional
  apartment?: string;        // ? Optional
  specialInstructions?: string; // ? Optional
}

// โœ… Create address with only required fields
const address1: Address = {
  id: 'ADDR_001',
  street: '123 Main St',
  city: 'Springfield',
  state: 'IL',
  zipCode: '62701'
};

// โœ… Create address with optional fields
const address2: Address = {
  id: 'ADDR_002',
  street: '456 Oak Ave',
  city: 'Portland',
  state: 'OR',
  zipCode: '97201',
  country: 'USA',              // โœ… Optional field included
  apartment: 'Apt 3B'          // โœ… Optional field included
};

// โŒ This would error - can't modify readonly property
// address1.id = 'NEW_ID'; // Error: Cannot assign to 'id' because it is a read-only property

// โœ… Other properties can be modified
address1.street = '789 Elm St';
console.log(`๐Ÿ“ Updated address: ${address1.street}`);

// ๐ŸŽฏ Function that uses optional properties
function formatAddress(address: Address): string {
  let formatted = `${address.street}`;
  
  if (address.apartment) {
    formatted += `, ${address.apartment}`;
  }
  
  formatted += `\n${address.city}, ${address.state} ${address.zipCode}`;
  
  if (address.country) {
    formatted += `\n${address.country}`;
  }
  
  return formatted;
}

console.log('๐Ÿ“ฌ Address 1:\n' + formatAddress(address1));
console.log('\n๐Ÿ“ฌ Address 2:\n' + formatAddress(address2));

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: E-commerce Product System

Letโ€™s build an e-commerce product system with interfaces:

// ๐Ÿ’ฐ Price information interface
interface Price {
  amount: number;
  currency: 'USD' | 'EUR' | 'GBP';
  discount?: number;           // Optional discount percentage
  readonly originalAmount: number; // Original price before discount
}

// ๐Ÿ“ฆ Dimensions interface
interface Dimensions {
  length: number;
  width: number;
  height: number;
  unit: 'cm' | 'inch';
}

// ๐Ÿท๏ธ Category interface
interface Category {
  id: string;
  name: string;
  parentCategory?: Category;   // Self-referential - categories can have parents
}

// ๐Ÿ›๏ธ Main Product interface
interface Product {
  readonly id: string;
  name: string;
  description: string;
  price: Price;
  inStock: boolean;
  stockQuantity: number;
  category: Category;
  tags: string[];
  dimensions?: Dimensions;      // Optional - not all products have dimensions
  weight?: number;             // Optional weight in kg
  images: string[];
  readonly createdAt: Date;
  updatedAt: Date;
}

// ๐Ÿ“Š Review interface
interface Review {
  id: string;
  productId: string;
  userId: string;
  rating: 1 | 2 | 3 | 4 | 5;  // Literal type union
  comment: string;
  helpful: number;
  verified: boolean;
  date: Date;
}

// ๐Ÿ›’ Shopping cart item interface
interface CartItem {
  product: Product;
  quantity: number;
  addedAt: Date;
}

// ๐Ÿ›๏ธ Shopping cart interface
interface ShoppingCart {
  id: string;
  userId: string;
  items: CartItem[];
  readonly createdAt: Date;
  lastModified: Date;
}

// ๐Ÿช Product service implementation
class ProductService {
  private products: Map<string, Product> = new Map();

  // โž• Add a new product
  addProduct(productData: Omit<Product, 'id' | 'createdAt' | 'updatedAt'>): Product {
    const product: Product = {
      ...productData,
      id: `PROD_${Date.now()}`,
      createdAt: new Date(),
      updatedAt: new Date()
    };

    this.products.set(product.id, product);
    console.log(`โœ… Added product: ${product.name} (${product.id})`);
    return product;
  }

  // ๐Ÿ’ฐ Calculate final price
  calculateFinalPrice(price: Price): number {
    if (price.discount && price.discount > 0) {
      const discountAmount = price.originalAmount * (price.discount / 100);
      return price.originalAmount - discountAmount;
    }
    return price.amount;
  }

  // ๐Ÿ” Search products by category
  findByCategory(categoryId: string): Product[] {
    return Array.from(this.products.values()).filter(
      product => product.category.id === categoryId
    );
  }

  // ๐Ÿ“Š Get product with reviews
  getProductWithReviews(productId: string, reviews: Review[]): ProductWithReviews {
    const product = this.products.get(productId);
    if (!product) {
      throw new Error(`Product ${productId} not found!`);
    }

    const productReviews = reviews.filter(r => r.productId === productId);
    const avgRating = productReviews.length > 0
      ? productReviews.reduce((sum, r) => sum + r.rating, 0) / productReviews.length
      : 0;

    return {
      ...product,
      reviews: productReviews,
      averageRating: avgRating,
      reviewCount: productReviews.length
    };
  }
}

// ๐ŸŒŸ Extended interface - Product with reviews
interface ProductWithReviews extends Product {
  reviews: Review[];
  averageRating: number;
  reviewCount: number;
}

// ๐Ÿ›’ Cart service implementation
class CartService {
  private carts: Map<string, ShoppingCart> = new Map();

  // ๐Ÿ›’ Create new cart
  createCart(userId: string): ShoppingCart {
    const cart: ShoppingCart = {
      id: `CART_${Date.now()}`,
      userId,
      items: [],
      createdAt: new Date(),
      lastModified: new Date()
    };

    this.carts.set(cart.id, cart);
    console.log(`๐Ÿ›’ Created cart for user ${userId}`);
    return cart;
  }

  // โž• Add item to cart
  addToCart(cartId: string, product: Product, quantity: number): void {
    const cart = this.carts.get(cartId);
    if (!cart) {
      throw new Error(`Cart ${cartId} not found!`);
    }

    const existingItem = cart.items.find(item => item.product.id === product.id);

    if (existingItem) {
      existingItem.quantity += quantity;
      console.log(`๐Ÿ“ฆ Updated quantity for ${product.name}: ${existingItem.quantity}`);
    } else {
      const cartItem: CartItem = {
        product,
        quantity,
        addedAt: new Date()
      };
      cart.items.push(cartItem);
      console.log(`โž• Added ${product.name} to cart`);
    }

    cart.lastModified = new Date();
  }

  // ๐Ÿ’ฐ Calculate cart total
  calculateCartTotal(cart: ShoppingCart): { subtotal: number; items: number } {
    let subtotal = 0;
    let items = 0;

    cart.items.forEach(item => {
      const price = item.product.price;
      const finalPrice = price.discount 
        ? price.originalAmount * (1 - price.discount / 100)
        : price.amount;
      
      subtotal += finalPrice * item.quantity;
      items += item.quantity;
    });

    return { subtotal, items };
  }
}

// ๐ŸŽฎ Demo the e-commerce system
const productService = new ProductService();
const cartService = new CartService();

// Create categories
const electronics: Category = {
  id: 'CAT_ELECTRONICS',
  name: 'Electronics'
};

const computers: Category = {
  id: 'CAT_COMPUTERS',
  name: 'Computers',
  parentCategory: electronics
};

// Create a product
const laptop = productService.addProduct({
  name: 'TypeScript Pro Laptop',
  description: 'The perfect laptop for TypeScript development',
  price: {
    amount: 1299.99,
    currency: 'USD',
    originalAmount: 1299.99,
    discount: 10 // 10% off!
  },
  inStock: true,
  stockQuantity: 50,
  category: computers,
  tags: ['laptop', 'typescript', 'development', 'portable'],
  dimensions: {
    length: 35,
    width: 25,
    height: 2,
    unit: 'cm'
  },
  weight: 1.5,
  images: ['laptop-front.jpg', 'laptop-side.jpg']
});

// Create some reviews
const reviews: Review[] = [
  {
    id: 'REV_001',
    productId: laptop.id,
    userId: 'USER_123',
    rating: 5,
    comment: 'Amazing laptop for TypeScript development! ๐Ÿš€',
    helpful: 42,
    verified: true,
    date: new Date()
  },
  {
    id: 'REV_002',
    productId: laptop.id,
    userId: 'USER_456',
    rating: 4,
    comment: 'Great performance, slightly heavy',
    helpful: 15,
    verified: true,
    date: new Date()
  }
];

// Get product with reviews
const productWithReviews = productService.getProductWithReviews(laptop.id, reviews);
console.log(`\nโญ ${productWithReviews.name} - Rating: ${productWithReviews.averageRating.toFixed(1)}/5 (${productWithReviews.reviewCount} reviews)`);

// Create cart and add items
const cart = cartService.createCart('USER_789');
cartService.addToCart(cart.id, laptop, 2);

// Calculate total
const cartTotal = cartService.calculateCartTotal(cart);
console.log(`\n๐Ÿ’ฐ Cart Total: $${cartTotal.subtotal.toFixed(2)} (${cartTotal.items} items)`);

๐ŸŽฎ Example 2: Game Development Interfaces

Letโ€™s create a game system with complex interfaces:

// ๐ŸŽฎ Vector interface for positions and directions
interface Vector2D {
  x: number;
  y: number;
}

interface Vector3D extends Vector2D {
  z: number;
}

// ๐ŸŽจ Color interface
interface Color {
  r: number; // 0-255
  g: number; // 0-255
  b: number; // 0-255
  a?: number; // Optional alpha channel
}

// ๐Ÿƒโ€โ™‚๏ธ Animation frame interface
interface AnimationFrame {
  duration: number; // milliseconds
  sprite: string;   // sprite filename
  offset?: Vector2D; // Optional position offset
}

// ๐ŸŽฌ Animation interface
interface Animation {
  name: string;
  frames: AnimationFrame[];
  loop: boolean;
  onComplete?: () => void; // Optional callback
}

// ๐ŸŽฎ Base game object interface
interface GameObject {
  id: string;
  name: string;
  position: Vector3D;
  rotation: number; // degrees
  scale: Vector2D;
  visible: boolean;
  layer: number;
  tags: Set<string>;
}

// ๐Ÿƒโ€โ™‚๏ธ Moveable game object
interface Moveable {
  velocity: Vector3D;
  acceleration: Vector3D;
  maxSpeed: number;
  move(delta: number): void;
}

// ๐Ÿ’ฅ Damageable interface
interface Damageable {
  health: number;
  maxHealth: number;
  armor: number;
  invulnerable: boolean;
  takeDamage(amount: number, source?: GameObject): void;
  heal(amount: number): void;
  onDeath?: () => void;
}

// ๐ŸŽฏ Interactive interface
interface Interactive {
  interactionRadius: number;
  canInteract: boolean;
  interact(actor: GameObject): void;
  getInteractionPrompt(): string;
}

// ๐Ÿ‘ค Character interface combining multiple interfaces
interface Character extends GameObject, Moveable, Damageable {
  level: number;
  experience: number;
  stats: CharacterStats;
  abilities: Ability[];
  inventory: InventoryItem[];
  currentAnimation?: Animation;
}

// ๐Ÿ“Š Character stats interface
interface CharacterStats {
  strength: number;
  agility: number;
  intelligence: number;
  vitality: number;
  luck: number;
}

// โœจ Ability interface
interface Ability {
  id: string;
  name: string;
  description: string;
  icon: string;
  cooldown: number; // seconds
  currentCooldown: number;
  manaCost: number;
  range: number;
  damage?: number;
  healing?: number;
  effects: Effect[];
  cast(caster: Character, target?: GameObject): void;
}

// ๐ŸŒŸ Effect interface
interface Effect {
  type: 'buff' | 'debuff' | 'damage' | 'heal';
  duration: number; // seconds
  value: number;
  stackable: boolean;
  icon: string;
  apply(target: Character): void;
  remove(target: Character): void;
}

// ๐ŸŽ’ Inventory item interface
interface InventoryItem {
  id: string;
  name: string;
  description: string;
  icon: string;
  stackable: boolean;
  quantity: number;
  weight: number;
  value: number; // gold value
  rarity: 'common' | 'uncommon' | 'rare' | 'epic' | 'legendary';
  onUse?: (user: Character) => void;
}

// โš”๏ธ Weapon interface extending inventory item
interface Weapon extends InventoryItem {
  damage: number;
  attackSpeed: number;
  range: number;
  weaponType: 'sword' | 'bow' | 'staff' | 'dagger';
  requirements: {
    level?: number;
    strength?: number;
    agility?: number;
  };
  enchantments?: Enchantment[];
}

// โœจ Enchantment interface
interface Enchantment {
  name: string;
  tier: number;
  effect: Effect;
  description: string;
}

// ๐ŸŒ Game world interface
interface GameWorld {
  name: string;
  dimensions: Vector3D;
  gravity: number;
  timeScale: number;
  weather: Weather;
  objects: Map<string, GameObject>;
  update(deltaTime: number): void;
  render(): void;
}

// ๐ŸŒฆ๏ธ Weather interface
interface Weather {
  type: 'sunny' | 'rainy' | 'stormy' | 'snowy' | 'foggy';
  intensity: number; // 0-1
  windDirection: Vector2D;
  windSpeed: number;
  temperature: number;
}

// ๐ŸŽฎ Player character implementation
class Player implements Character {
  id: string;
  name: string;
  position: Vector3D;
  rotation: number = 0;
  scale: Vector2D = { x: 1, y: 1 };
  visible: boolean = true;
  layer: number = 1;
  tags: Set<string> = new Set(['player', 'character']);
  
  velocity: Vector3D = { x: 0, y: 0, z: 0 };
  acceleration: Vector3D = { x: 0, y: 0, z: 0 };
  maxSpeed: number = 5;
  
  health: number;
  maxHealth: number;
  armor: number = 0;
  invulnerable: boolean = false;
  
  level: number = 1;
  experience: number = 0;
  stats: CharacterStats;
  abilities: Ability[] = [];
  inventory: InventoryItem[] = [];
  currentAnimation?: Animation;

  constructor(name: string, position: Vector3D) {
    this.id = `PLAYER_${Date.now()}`;
    this.name = name;
    this.position = position;
    this.maxHealth = 100;
    this.health = this.maxHealth;
    
    this.stats = {
      strength: 10,
      agility: 10,
      intelligence: 10,
      vitality: 10,
      luck: 5
    };

    console.log(`๐ŸŽฎ Player ${name} created at position (${position.x}, ${position.y}, ${position.z})`);
  }

  move(delta: number): void {
    // Apply acceleration to velocity
    this.velocity.x += this.acceleration.x * delta;
    this.velocity.y += this.acceleration.y * delta;
    this.velocity.z += this.acceleration.z * delta;

    // Clamp to max speed
    const speed = Math.sqrt(
      this.velocity.x ** 2 + 
      this.velocity.y ** 2 + 
      this.velocity.z ** 2
    );
    
    if (speed > this.maxSpeed) {
      const scale = this.maxSpeed / speed;
      this.velocity.x *= scale;
      this.velocity.y *= scale;
      this.velocity.z *= scale;
    }

    // Update position
    this.position.x += this.velocity.x * delta;
    this.position.y += this.velocity.y * delta;
    this.position.z += this.velocity.z * delta;
  }

  takeDamage(amount: number, source?: GameObject): void {
    if (this.invulnerable) {
      console.log(`๐Ÿ›ก๏ธ ${this.name} is invulnerable!`);
      return;
    }

    const actualDamage = Math.max(0, amount - this.armor);
    this.health = Math.max(0, this.health - actualDamage);
    
    console.log(`๐Ÿ’ฅ ${this.name} takes ${actualDamage} damage! Health: ${this.health}/${this.maxHealth}`);
    
    if (this.health === 0 && this.onDeath) {
      this.onDeath();
    }
  }

  heal(amount: number): void {
    const oldHealth = this.health;
    this.health = Math.min(this.maxHealth, this.health + amount);
    const healed = this.health - oldHealth;
    
    if (healed > 0) {
      console.log(`๐Ÿ’š ${this.name} healed for ${healed}! Health: ${this.health}/${this.maxHealth}`);
    }
  }

  onDeath(): void {
    console.log(`โ˜ ๏ธ ${this.name} has been defeated!`);
  }

  addToInventory(item: InventoryItem): boolean {
    if (item.stackable) {
      const existing = this.inventory.find(i => i.id === item.id);
      if (existing) {
        existing.quantity += item.quantity;
        console.log(`๐Ÿ“ฆ Added ${item.quantity} ${item.name} (total: ${existing.quantity})`);
        return true;
      }
    }

    this.inventory.push(item);
    console.log(`๐ŸŽ’ Added ${item.name} to inventory`);
    return true;
  }

  equipWeapon(weapon: Weapon): boolean {
    // Check requirements
    if (weapon.requirements.level && this.level < weapon.requirements.level) {
      console.log(`โŒ Need level ${weapon.requirements.level} to equip ${weapon.name}`);
      return false;
    }

    if (weapon.requirements.strength && this.stats.strength < weapon.requirements.strength) {
      console.log(`โŒ Need ${weapon.requirements.strength} strength to equip ${weapon.name}`);
      return false;
    }

    console.log(`โš”๏ธ Equipped ${weapon.name}!`);
    return true;
  }
}

// ๐Ÿช NPC with interactive interface
class Merchant implements GameObject, Interactive {
  id: string;
  name: string;
  position: Vector3D;
  rotation: number = 0;
  scale: Vector2D = { x: 1, y: 1 };
  visible: boolean = true;
  layer: number = 1;
  tags: Set<string> = new Set(['npc', 'merchant']);
  
  interactionRadius: number = 2;
  canInteract: boolean = true;
  
  private inventory: InventoryItem[] = [];

  constructor(name: string, position: Vector3D) {
    this.id = `MERCHANT_${Date.now()}`;
    this.name = name;
    this.position = position;
    
    // Stock some items
    this.stockInventory();
  }

  private stockInventory(): void {
    // Create a weapon
    const ironSword: Weapon = {
      id: 'WEAPON_IRON_SWORD',
      name: 'Iron Sword',
      description: 'A sturdy iron sword',
      icon: 'iron-sword.png',
      stackable: false,
      quantity: 1,
      weight: 3,
      value: 100,
      rarity: 'common',
      damage: 10,
      attackSpeed: 1.5,
      range: 1.5,
      weaponType: 'sword',
      requirements: {
        level: 1,
        strength: 5
      }
    };

    // Create a potion
    const healthPotion: InventoryItem = {
      id: 'ITEM_HEALTH_POTION',
      name: 'Health Potion',
      description: 'Restores 50 HP',
      icon: 'health-potion.png',
      stackable: true,
      quantity: 10,
      weight: 0.5,
      value: 50,
      rarity: 'common',
      onUse: (user: Character) => {
        user.heal(50);
        console.log(`๐Ÿงช ${user.name} drinks a health potion!`);
      }
    };

    this.inventory.push(ironSword, healthPotion);
  }

  interact(actor: GameObject): void {
    if (actor instanceof Player) {
      console.log(`๐Ÿช ${this.name}: "Welcome, ${actor.name}! Care to see my wares?"`);
      this.showInventory();
    }
  }

  getInteractionPrompt(): string {
    return `Talk to ${this.name} (E)`;
  }

  private showInventory(): void {
    console.log('\n๐Ÿ“ฆ Merchant Inventory:');
    this.inventory.forEach(item => {
      const rarityEmoji = {
        common: 'โšช',
        uncommon: '๐ŸŸข',
        rare: '๐Ÿ”ต',
        epic: '๐ŸŸฃ',
        legendary: '๐ŸŸก'
      };
      
      console.log(`${rarityEmoji[item.rarity]} ${item.name} - ${item.value} gold`);
    });
  }
}

// ๐ŸŽฎ Demo the game system
console.log('๐ŸŽฎ GAME SYSTEM DEMO ๐ŸŽฎ\n');

// Create player
const player = new Player('Hero', { x: 0, y: 0, z: 0 });

// Create merchant
const merchant = new Merchant('Grizelda the Trader', { x: 5, y: 0, z: 0 });

// Test damage and healing
player.takeDamage(30);
player.heal(20);

// Test inventory
const magicRing: InventoryItem = {
  id: 'ITEM_MAGIC_RING',
  name: 'Ring of Power',
  description: 'Increases all stats by 5',
  icon: 'magic-ring.png',
  stackable: false,
  quantity: 1,
  weight: 0.1,
  value: 500,
  rarity: 'rare'
};

player.addToInventory(magicRing);

// Create and equip a weapon
const legendaryStaff: Weapon = {
  id: 'WEAPON_LEGENDARY_STAFF',
  name: 'Staff of the Archmage',
  description: 'A powerful magical staff',
  icon: 'archmage-staff.png',
  stackable: false,
  quantity: 1,
  weight: 2,
  value: 5000,
  rarity: 'legendary',
  damage: 50,
  attackSpeed: 0.8,
  range: 5,
  weaponType: 'staff',
  requirements: {
    level: 10,
    intelligence: 20
  },
  enchantments: [
    {
      name: 'Frost Damage',
      tier: 3,
      effect: {
        type: 'damage',
        duration: 3,
        value: 10,
        stackable: false,
        icon: 'frost.png',
        apply: (target) => console.log(`โ„๏ธ ${target.name} is frozen!`),
        remove: (target) => console.log(`๐Ÿ”ฅ ${target.name} thaws out`)
      },
      description: 'Deals frost damage over time'
    }
  ]
};

// Try to equip (will fail due to level requirement)
player.equipWeapon(legendaryStaff);

// Check if player can interact with merchant
const distance = Math.sqrt(
  Math.pow(player.position.x - merchant.position.x, 2) +
  Math.pow(player.position.y - merchant.position.y, 2)
);

if (distance <= merchant.interactionRadius) {
  console.log(`\n๐Ÿ’ฌ ${merchant.getInteractionPrompt()}`);
  merchant.interact(player);
} else {
  console.log(`\n๐Ÿšถ Too far from merchant (distance: ${distance.toFixed(1)})`);
}

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Advanced Topic 1: Index Signatures and Mapped Types

Create flexible interfaces with dynamic properties:

// ๐Ÿ”‘ Index signature for dynamic properties
interface Dictionary<T> {
  [key: string]: T;
}

// ๐Ÿ“Š Analytics data with flexible metrics
interface AnalyticsEvent {
  eventName: string;
  timestamp: Date;
  userId: string;
  properties: Dictionary<string | number | boolean>;
  metadata?: Dictionary<any>;
}

// ๐ŸŽฏ Strongly typed configuration
interface AppConfig {
  appName: string;
  version: string;
  features: {
    [featureName: string]: {
      enabled: boolean;
      config?: Dictionary<any>;
    };
  };
}

// ๐ŸŒ API response with dynamic fields
interface ApiResponse<T> {
  data: T;
  meta: {
    timestamp: Date;
    requestId: string;
    [key: string]: any; // Additional metadata
  };
  errors?: Array<{
    code: string;
    message: string;
    field?: string;
  }>;
}

// ๐Ÿ“ Form data with validation
interface FormField {
  value: any;
  touched: boolean;
  errors: string[];
  validators: Array<(value: any) => string | null>;
}

interface Form {
  fields: {
    [fieldName: string]: FormField;
  };
  isValid: boolean;
  isDirty: boolean;
}

// ๐ŸŽจ Theme system with nested properties
interface Theme {
  colors: {
    primary: string;
    secondary: string;
    [colorName: string]: string;
  };
  spacing: {
    small: number;
    medium: number;
    large: number;
    [size: string]: number;
  };
  components: {
    [componentName: string]: {
      [property: string]: string | number;
    };
  };
}

// ๐Ÿ› ๏ธ Implementation examples
class AnalyticsService {
  private events: AnalyticsEvent[] = [];

  track(eventName: string, userId: string, properties: Dictionary<any>): void {
    const event: AnalyticsEvent = {
      eventName,
      timestamp: new Date(),
      userId,
      properties
    };

    this.events.push(event);
    console.log(`๐Ÿ“Š Tracked: ${eventName} for user ${userId}`);
  }

  // ๐Ÿ” Get events with specific property
  getEventsWithProperty(propertyName: string): AnalyticsEvent[] {
    return this.events.filter(event => 
      propertyName in event.properties
    );
  }
}

// ๐ŸŽจ Theme implementation
const darkTheme: Theme = {
  colors: {
    primary: '#007bff',
    secondary: '#6c757d',
    background: '#1a1a1a',
    text: '#ffffff',
    error: '#dc3545',
    success: '#28a745'
  },
  spacing: {
    small: 8,
    medium: 16,
    large: 24,
    extraLarge: 32
  },
  components: {
    button: {
      borderRadius: 4,
      padding: 12,
      fontSize: 16
    },
    card: {
      borderRadius: 8,
      shadowBlur: 10,
      padding: 16
    }
  }
};

// ๐Ÿ”ง Form builder with dynamic fields
class FormBuilder {
  createForm(fieldConfigs: Dictionary<Partial<FormField>>): Form {
    const fields: Dictionary<FormField> = {};

    Object.entries(fieldConfigs).forEach(([name, config]) => {
      fields[name] = {
        value: config.value ?? '',
        touched: config.touched ?? false,
        errors: config.errors ?? [],
        validators: config.validators ?? []
      };
    });

    return {
      fields,
      isValid: this.validateForm(fields),
      isDirty: false
    };
  }

  private validateForm(fields: Dictionary<FormField>): boolean {
    return Object.values(fields).every(field => 
      field.errors.length === 0
    );
  }
}

// ๐ŸŽฎ Demo
const analytics = new AnalyticsService();

// Track various events
analytics.track('page_view', 'USER_123', {
  page: '/products',
  referrer: 'google.com',
  device: 'mobile',
  sessionDuration: 145
});

analytics.track('purchase', 'USER_456', {
  productId: 'PROD_789',
  amount: 99.99,
  currency: 'USD',
  paymentMethod: 'credit_card',
  couponUsed: true
});

// Form example
const formBuilder = new FormBuilder();
const loginForm = formBuilder.createForm({
  email: {
    value: '',
    validators: [
      (value) => !value ? 'Email is required' : null,
      (value) => !value.includes('@') ? 'Invalid email' : null
    ]
  },
  password: {
    value: '',
    validators: [
      (value) => !value ? 'Password is required' : null,
      (value) => value.length < 8 ? 'Min 8 characters' : null
    ]
  }
});

console.log('๐Ÿ“ Form created with fields:', Object.keys(loginForm.fields));

๐Ÿ—๏ธ Advanced Topic 2: Discriminated Unions with Interfaces

Create type-safe state machines and variant types:

// ๐Ÿšฆ Network request states
interface RequestPending {
  status: 'pending';
  startedAt: Date;
}

interface RequestSuccess<T> {
  status: 'success';
  data: T;
  completedAt: Date;
  cached: boolean;
}

interface RequestError {
  status: 'error';
  error: Error;
  code: string;
  retryCount: number;
  canRetry: boolean;
}

interface RequestIdle {
  status: 'idle';
}

// Union type of all states
type RequestState<T> = 
  | RequestIdle
  | RequestPending
  | RequestSuccess<T>
  | RequestError;

// ๐ŸŽฏ Type-safe state handling
function handleRequestState<T>(state: RequestState<T>): string {
  switch (state.status) {
    case 'idle':
      return '๐Ÿ’ค Waiting to start...';
    
    case 'pending':
      const elapsed = Date.now() - state.startedAt.getTime();
      return `โณ Loading... (${Math.round(elapsed / 1000)}s)`;
    
    case 'success':
      return `โœ… Success! ${state.cached ? '(from cache)' : '(fresh data)'}`;
    
    case 'error':
      return `โŒ Error: ${state.error.message} ${state.canRetry ? '(retrying...)' : ''}`;
  }
}

// ๐ŸŽฎ Game state variants
interface MenuState {
  type: 'menu';
  selectedOption: number;
  options: string[];
}

interface PlayingState {
  type: 'playing';
  level: number;
  score: number;
  lives: number;
  isPaused: boolean;
}

interface GameOverState {
  type: 'gameOver';
  finalScore: number;
  highScore: number;
  newRecord: boolean;
}

interface LoadingState {
  type: 'loading';
  progress: number;
  message: string;
}

type GameState = MenuState | PlayingState | GameOverState | LoadingState;

// ๐ŸŽฎ Game state machine
class GameStateMachine {
  private currentState: GameState;
  private stateHistory: GameState[] = [];

  constructor() {
    this.currentState = {
      type: 'menu',
      selectedOption: 0,
      options: ['New Game', 'Continue', 'Options', 'Quit']
    };
  }

  transition(newState: GameState): void {
    console.log(`๐Ÿ”„ State transition: ${this.currentState.type} โ†’ ${newState.type}`);
    this.stateHistory.push(this.currentState);
    this.currentState = newState;
    this.handleStateChange();
  }

  private handleStateChange(): void {
    switch (this.currentState.type) {
      case 'menu':
        console.log('๐ŸŽฎ Main Menu');
        this.currentState.options.forEach((opt, i) => {
          const selected = i === this.currentState.selectedOption ? 'โ–ถ๏ธ' : '  ';
          console.log(`${selected} ${opt}`);
        });
        break;

      case 'loading':
        console.log(`โณ Loading: ${this.currentState.message} (${this.currentState.progress}%)`);
        break;

      case 'playing':
        console.log(`๐ŸŽฎ Playing Level ${this.currentState.level}`);
        console.log(`   Score: ${this.currentState.score} | Lives: ${'โค๏ธ'.repeat(this.currentState.lives)}`);
        if (this.currentState.isPaused) {
          console.log('   โธ๏ธ PAUSED');
        }
        break;

      case 'gameOver':
        console.log('๐Ÿ’€ GAME OVER');
        console.log(`   Final Score: ${this.currentState.finalScore}`);
        if (this.currentState.newRecord) {
          console.log('   ๐Ÿ† NEW HIGH SCORE!');
        }
        break;
    }
  }

  // Type-safe state updates
  updateState<K extends GameState['type']>(
    type: K,
    updater: (state: Extract<GameState, { type: K }>) => void
  ): void {
    if (this.currentState.type === type) {
      updater(this.currentState as any);
      this.handleStateChange();
    }
  }
}

// ๐Ÿ“Š Data processing pipeline states
interface DataSource {
  kind: 'source';
  url: string;
  headers?: Dictionary<string>;
}

interface DataTransform {
  kind: 'transform';
  operation: 'filter' | 'map' | 'reduce' | 'sort';
  config: any;
}

interface DataSink {
  kind: 'sink';
  destination: 'database' | 'file' | 'api';
  options: Dictionary<any>;
}

type PipelineStage = DataSource | DataTransform | DataSink;

interface Pipeline {
  name: string;
  stages: PipelineStage[];
  schedule?: string;
  enabled: boolean;
}

// ๐Ÿ”ง Pipeline processor
class PipelineProcessor {
  async processPipeline(pipeline: Pipeline): Promise<void> {
    console.log(`๐Ÿš€ Starting pipeline: ${pipeline.name}`);

    for (const stage of pipeline.stages) {
      switch (stage.kind) {
        case 'source':
          console.log(`๐Ÿ“ฅ Fetching data from: ${stage.url}`);
          // Fetch data logic
          break;

        case 'transform':
          console.log(`๐Ÿ”„ Applying ${stage.operation} transformation`);
          // Transform data logic
          break;

        case 'sink':
          console.log(`๐Ÿ“ค Writing data to: ${stage.destination}`);
          // Write data logic
          break;
      }
    }

    console.log(`โœ… Pipeline ${pipeline.name} completed`);
  }
}

// ๐ŸŽฎ Demo discriminated unions
const game = new GameStateMachine();

// Start game
game.transition({
  type: 'loading',
  progress: 0,
  message: 'Initializing game engine...'
});

// Simulate loading progress
setTimeout(() => {
  game.transition({
    type: 'loading',
    progress: 50,
    message: 'Loading assets...'
  });
}, 1000);

setTimeout(() => {
  game.transition({
    type: 'playing',
    level: 1,
    score: 0,
    lives: 3,
    isPaused: false
  });

  // Update score while playing
  game.updateState('playing', state => {
    state.score += 100;
  });
}, 2000);

// Data pipeline example
const etlPipeline: Pipeline = {
  name: 'User Analytics ETL',
  enabled: true,
  stages: [
    {
      kind: 'source',
      url: 'https://api.example.com/users',
      headers: {
        'Authorization': 'Bearer token'
      }
    },
    {
      kind: 'transform',
      operation: 'filter',
      config: { active: true }
    },
    {
      kind: 'transform',
      operation: 'map',
      config: { fields: ['id', 'name', 'email'] }
    },
    {
      kind: 'sink',
      destination: 'database',
      options: {
        table: 'analytics_users',
        upsert: true
      }
    }
  ]
};

const processor = new PipelineProcessor();
// processor.processPipeline(etlPipeline);

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Interface vs Type Confusion

// โŒ Using type when interface would be better
type UserType = {
  name: string;
  age: number;
};

// โœ… Use interface for object shapes
interface UserInterface {
  name: string;
  age: number;
}

// โœ… Interfaces can be extended
interface AdminUser extends UserInterface {
  permissions: string[];
}

// โœ… Interfaces can be merged (declaration merging)
interface UserInterface {
  email?: string; // This gets merged!
}

// โŒ Types can't be merged
type UserType = {
  email?: string; // Error: Duplicate identifier
};

๐Ÿคฏ Pitfall 2: Optional vs Undefined

// โŒ Confusing optional with undefined
interface BadConfig {
  apiUrl: string | undefined; // Must provide, but can be undefined
  timeout?: number;           // Truly optional
}

// This is required but can be undefined
const config1: BadConfig = {
  apiUrl: undefined,
  // timeout is optional, so we can omit it
};

// โŒ This errors - apiUrl is required!
// const config2: BadConfig = {
//   timeout: 5000
// };

// โœ… Clear intent
interface GoodConfig {
  apiUrl: string;         // Required and must have value
  timeout?: number;       // Optional
  retry?: boolean;        // Optional
}

๐Ÿ”„ Pitfall 3: Excess Property Checks

interface Point {
  x: number;
  y: number;
}

// โŒ Excess property check fails
const point1: Point = {
  x: 10,
  y: 20,
  z: 30 // Error: 'z' does not exist in type 'Point'
};

// โœ… Ways to handle extra properties

// 1. Use index signature
interface FlexiblePoint {
  x: number;
  y: number;
  [key: string]: number; // Allow any additional number properties
}

// 2. Type assertion
const point2 = {
  x: 10,
  y: 20,
  z: 30
} as Point; // Works but loses type safety for 'z'

// 3. Separate variable
const point3Data = { x: 10, y: 20, z: 30 };
const point3: Point = point3Data; // Works!

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use Interfaces for Object Shapes: Prefer interfaces over type aliases for objects
  2. ๐Ÿ“ Document with JSDoc: Add comments to interface properties
  3. ๐Ÿ”ง Keep Interfaces Focused: Single responsibility principle applies
  4. โ™ป๏ธ Extend and Compose: Build complex interfaces from simple ones
  5. ๐Ÿ›ก๏ธ Use Readonly When Appropriate: Prevent unwanted mutations

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Task Management System

Create a comprehensive task management system with interfaces:

๐Ÿ“‹ Requirements:

  • โœ… Task interface with title, description, status, priority
  • ๐Ÿ‘ค User interface with roles and permissions
  • ๐Ÿ“Š Project interface containing multiple tasks
  • ๐Ÿ”” Notification system for task updates
  • ๐Ÿ“ˆ Analytics interface for tracking productivity

๐Ÿš€ Bonus Points:

  • Add task dependencies
  • Implement recurring tasks
  • Create a comment system
  • Add time tracking

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐Ÿ‘ค User interfaces
interface User {
  id: string;
  name: string;
  email: string;
  avatar?: string;
  role: UserRole;
  permissions: Set<Permission>;
  preferences: UserPreferences;
  createdAt: Date;
  lastLogin: Date;
}

interface UserRole {
  id: string;
  name: 'admin' | 'manager' | 'member' | 'guest';
  level: number;
}

type Permission = 
  | 'create:task'
  | 'edit:task'
  | 'delete:task'
  | 'assign:task'
  | 'create:project'
  | 'edit:project'
  | 'delete:project'
  | 'view:analytics';

interface UserPreferences {
  theme: 'light' | 'dark' | 'auto';
  notifications: {
    email: boolean;
    push: boolean;
    slack: boolean;
  };
  language: string;
  timezone: string;
}

// ๐Ÿ“‹ Task interfaces
interface Task {
  id: string;
  title: string;
  description: string;
  status: TaskStatus;
  priority: TaskPriority;
  assignee?: User;
  reporter: User;
  projectId: string;
  tags: string[];
  dueDate?: Date;
  estimatedHours?: number;
  actualHours?: number;
  dependencies: string[]; // Task IDs
  attachments: Attachment[];
  comments: Comment[];
  createdAt: Date;
  updatedAt: Date;
  completedAt?: Date;
}

type TaskStatus = 
  | 'backlog'
  | 'todo'
  | 'in_progress'
  | 'review'
  | 'done'
  | 'cancelled';

type TaskPriority = 'low' | 'medium' | 'high' | 'urgent';

interface Attachment {
  id: string;
  filename: string;
  url: string;
  size: number;
  mimeType: string;
  uploadedBy: User;
  uploadedAt: Date;
}

interface Comment {
  id: string;
  content: string;
  author: User;
  createdAt: Date;
  editedAt?: Date;
  mentions: User[];
  reactions: Reaction[];
}

interface Reaction {
  emoji: string;
  users: User[];
}

// ๐Ÿ“Š Project interfaces
interface Project {
  id: string;
  name: string;
  description: string;
  code: string; // Project code like "PROJ-123"
  status: 'planning' | 'active' | 'on_hold' | 'completed' | 'cancelled';
  owner: User;
  members: ProjectMember[];
  startDate: Date;
  targetDate?: Date;
  actualEndDate?: Date;
  budget?: number;
  tags: string[];
  settings: ProjectSettings;
}

interface ProjectMember {
  user: User;
  role: 'owner' | 'admin' | 'member' | 'viewer';
  joinedAt: Date;
}

interface ProjectSettings {
  isPrivate: boolean;
  allowGuestAccess: boolean;
  requireApproval: boolean;
  defaultTaskStatus: TaskStatus;
  taskPrefix: string;
}

// ๐Ÿ”„ Recurring task interfaces
interface RecurringTask extends Omit<Task, 'id' | 'status'> {
  recurrenceRule: RecurrenceRule;
  instances: Task[];
  nextOccurrence?: Date;
}

interface RecurrenceRule {
  frequency: 'daily' | 'weekly' | 'monthly' | 'yearly';
  interval: number;
  daysOfWeek?: number[]; // 0-6 for weekly
  dayOfMonth?: number; // 1-31 for monthly
  endDate?: Date;
  maxOccurrences?: number;
}

// ๐Ÿ”” Notification interfaces
interface Notification {
  id: string;
  type: NotificationType;
  recipient: User;
  title: string;
  message: string;
  data: NotificationData;
  read: boolean;
  createdAt: Date;
  readAt?: Date;
}

type NotificationType = 
  | 'task:assigned'
  | 'task:mentioned'
  | 'task:completed'
  | 'task:overdue'
  | 'project:added'
  | 'comment:new'
  | 'comment:mention';

interface NotificationData {
  taskId?: string;
  projectId?: string;
  commentId?: string;
  userId?: string;
  [key: string]: any;
}

// ๐Ÿ“ˆ Analytics interfaces
interface TaskAnalytics {
  totalTasks: number;
  completedTasks: number;
  overdueTasks: number;
  averageCompletionTime: number; // hours
  tasksByStatus: Record<TaskStatus, number>;
  tasksByPriority: Record<TaskPriority, number>;
  velocityTrend: VelocityPoint[];
}

interface VelocityPoint {
  date: Date;
  completed: number;
  created: number;
}

interface UserProductivity {
  userId: string;
  tasksCompleted: number;
  averageTaskTime: number;
  punctualityRate: number; // % of tasks completed on time
  currentStreak: number; // days
  longestStreak: number;
}

interface ProjectHealth {
  projectId: string;
  healthScore: number; // 0-100
  onTrack: boolean;
  riskFactors: string[];
  burndownChart: BurndownPoint[];
  teamUtilization: number; // percentage
}

interface BurndownPoint {
  date: Date;
  remainingTasks: number;
  idealRemaining: number;
}

// ๐Ÿ› ๏ธ Task management service
class TaskManagementService {
  private tasks: Map<string, Task> = new Map();
  private projects: Map<string, Project> = new Map();
  private users: Map<string, User> = new Map();
  private notifications: Notification[] = [];

  // ๐Ÿ“‹ Create a new task
  createTask(taskData: Omit<Task, 'id' | 'createdAt' | 'updatedAt' | 'comments'>): Task {
    const task: Task = {
      ...taskData,
      id: `TASK_${Date.now()}`,
      createdAt: new Date(),
      updatedAt: new Date(),
      comments: []
    };

    this.tasks.set(task.id, task);
    
    // Send notification if assigned
    if (task.assignee) {
      this.sendNotification({
        type: 'task:assigned',
        recipient: task.assignee,
        title: 'New Task Assigned',
        message: `You've been assigned to "${task.title}"`,
        data: { taskId: task.id, projectId: task.projectId }
      });
    }

    console.log(`โœ… Created task: ${task.title} (${task.id})`);
    return task;
  }

  // ๐Ÿ”„ Update task status
  updateTaskStatus(taskId: string, newStatus: TaskStatus, user: User): void {
    const task = this.tasks.get(taskId);
    if (!task) throw new Error(`Task ${taskId} not found`);

    const oldStatus = task.status;
    task.status = newStatus;
    task.updatedAt = new Date();

    if (newStatus === 'done') {
      task.completedAt = new Date();
      console.log(`โœ… Task completed: ${task.title}`);
      
      // Check dependencies
      this.checkDependentTasks(taskId);
    }

    console.log(`๐Ÿ“Š Task ${task.title}: ${oldStatus} โ†’ ${newStatus}`);
  }

  // ๐Ÿ”— Check dependent tasks
  private checkDependentTasks(completedTaskId: string): void {
    this.tasks.forEach(task => {
      if (task.dependencies.includes(completedTaskId)) {
        const allDependenciesComplete = task.dependencies.every(depId => {
          const depTask = this.tasks.get(depId);
          return depTask?.status === 'done';
        });

        if (allDependenciesComplete && task.status === 'backlog') {
          task.status = 'todo';
          console.log(`๐Ÿ”“ Task "${task.title}" is now unblocked!`);
        }
      }
    });
  }

  // ๐Ÿ’ฌ Add comment to task
  addComment(taskId: string, author: User, content: string): Comment {
    const task = this.tasks.get(taskId);
    if (!task) throw new Error(`Task ${taskId} not found`);

    const comment: Comment = {
      id: `COMMENT_${Date.now()}`,
      content,
      author,
      createdAt: new Date(),
      mentions: this.extractMentions(content),
      reactions: []
    };

    task.comments.push(comment);
    task.updatedAt = new Date();

    // Notify mentioned users
    comment.mentions.forEach(user => {
      this.sendNotification({
        type: 'comment:mention',
        recipient: user,
        title: 'You were mentioned',
        message: `${author.name} mentioned you in "${task.title}"`,
        data: { taskId, commentId: comment.id }
      });
    });

    console.log(`๐Ÿ’ฌ Comment added to task ${task.title}`);
    return comment;
  }

  // ๐Ÿ‘ฅ Extract mentions from text
  private extractMentions(text: string): User[] {
    const mentionPattern = /@(\w+)/g;
    const mentions: User[] = [];
    let match;

    while ((match = mentionPattern.exec(text)) !== null) {
      const username = match[1];
      // In real app, would look up user by username
      // For demo, we'll skip this
    }

    return mentions;
  }

  // ๐Ÿ”” Send notification
  private sendNotification(data: Omit<Notification, 'id' | 'read' | 'createdAt'>): void {
    const notification: Notification = {
      ...data,
      id: `NOTIF_${Date.now()}`,
      read: false,
      createdAt: new Date()
    };

    this.notifications.push(notification);
    console.log(`๐Ÿ”” Notification sent to ${data.recipient.name}: ${data.title}`);
  }

  // ๐Ÿ“Š Get project analytics
  getProjectAnalytics(projectId: string): TaskAnalytics {
    const projectTasks = Array.from(this.tasks.values())
      .filter(task => task.projectId === projectId);

    const now = new Date();
    const analytics: TaskAnalytics = {
      totalTasks: projectTasks.length,
      completedTasks: projectTasks.filter(t => t.status === 'done').length,
      overdueTasks: projectTasks.filter(t => 
        t.dueDate && t.dueDate < now && t.status !== 'done'
      ).length,
      averageCompletionTime: this.calculateAverageCompletionTime(projectTasks),
      tasksByStatus: this.groupTasksByStatus(projectTasks),
      tasksByPriority: this.groupTasksByPriority(projectTasks),
      velocityTrend: this.calculateVelocityTrend(projectTasks)
    };

    return analytics;
  }

  private calculateAverageCompletionTime(tasks: Task[]): number {
    const completedTasks = tasks.filter(t => t.completedAt);
    if (completedTasks.length === 0) return 0;

    const totalTime = completedTasks.reduce((sum, task) => {
      const time = task.completedAt!.getTime() - task.createdAt.getTime();
      return sum + time;
    }, 0);

    return totalTime / completedTasks.length / (1000 * 60 * 60); // Convert to hours
  }

  private groupTasksByStatus(tasks: Task[]): Record<TaskStatus, number> {
    const groups: Record<TaskStatus, number> = {
      backlog: 0,
      todo: 0,
      in_progress: 0,
      review: 0,
      done: 0,
      cancelled: 0
    };

    tasks.forEach(task => {
      groups[task.status]++;
    });

    return groups;
  }

  private groupTasksByPriority(tasks: Task[]): Record<TaskPriority, number> {
    const groups: Record<TaskPriority, number> = {
      low: 0,
      medium: 0,
      high: 0,
      urgent: 0
    };

    tasks.forEach(task => {
      groups[task.priority]++;
    });

    return groups;
  }

  private calculateVelocityTrend(tasks: Task[]): VelocityPoint[] {
    // Simplified - in real app would calculate daily/weekly velocity
    return [];
  }
}

// ๐ŸŽฎ Demo the task management system
console.log('๐Ÿ“‹ TASK MANAGEMENT SYSTEM DEMO ๐Ÿ“‹\n');

const service = new TaskManagementService();

// Create users
const alice: User = {
  id: 'USER_1',
  name: 'Alice Developer',
  email: '[email protected]',
  role: { id: 'ROLE_1', name: 'member', level: 2 },
  permissions: new Set(['create:task', 'edit:task']),
  preferences: {
    theme: 'dark',
    notifications: { email: true, push: true, slack: false },
    language: 'en',
    timezone: 'UTC'
  },
  createdAt: new Date(),
  lastLogin: new Date()
};

const bob: User = {
  id: 'USER_2',
  name: 'Bob Manager',
  email: '[email protected]',
  role: { id: 'ROLE_2', name: 'manager', level: 3 },
  permissions: new Set(['create:task', 'edit:task', 'delete:task', 'assign:task']),
  preferences: {
    theme: 'light',
    notifications: { email: true, push: false, slack: true },
    language: 'en',
    timezone: 'UTC'
  },
  createdAt: new Date(),
  lastLogin: new Date()
};

// Create tasks
const task1 = service.createTask({
  title: 'Implement user authentication',
  description: 'Add JWT-based authentication to the API',
  status: 'todo',
  priority: 'high',
  assignee: alice,
  reporter: bob,
  projectId: 'PROJ_1',
  tags: ['backend', 'security'],
  dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), // 1 week
  estimatedHours: 16,
  dependencies: [],
  attachments: []
});

const task2 = service.createTask({
  title: 'Create login UI',
  description: 'Design and implement the login page',
  status: 'backlog',
  priority: 'medium',
  assignee: alice,
  reporter: bob,
  projectId: 'PROJ_1',
  tags: ['frontend', 'ui'],
  estimatedHours: 8,
  dependencies: [task1.id], // Depends on auth implementation
  attachments: []
});

// Update task status
service.updateTaskStatus(task1.id, 'in_progress', alice);

// Add comment
service.addComment(
  task1.id,
  alice,
  'Started working on this. Using JWT with refresh tokens for better security.'
);

// Get analytics
const analytics = service.getProjectAnalytics('PROJ_1');
console.log('\n๐Ÿ“Š Project Analytics:');
console.log(`   Total tasks: ${analytics.totalTasks}`);
console.log(`   Completed: ${analytics.completedTasks}`);
console.log(`   Overdue: ${analytics.overdueTasks}`);
console.log(`   Task distribution:`, analytics.tasksByStatus);

๐ŸŽ“ Key Takeaways

Youโ€™ve mastered TypeScript interfaces! Hereโ€™s what you can now do:

  • โœ… Define object shapes with precision ๐ŸŽฏ
  • โœ… Use optional and readonly properties effectively ๐Ÿ”’
  • โœ… Extend and compose interfaces for complex types ๐Ÿ—๏ธ
  • โœ… Create type-safe contracts for your code ๐Ÿ“œ
  • โœ… Build discriminated unions for state management ๐Ÿšฆ

Remember: Interfaces are the foundation of type-safe TypeScript - use them everywhere! ๐ŸŒŸ

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered TypeScript interfaces!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice with the task management exercise above
  2. ๐Ÿ—๏ธ Design interfaces for your own projects
  3. ๐Ÿ“š Move on to our next tutorial: Interface vs Type Alias: When to Use Each
  4. ๐ŸŒŸ Experiment with advanced interface patterns!

Remember: Great TypeScript code starts with well-designed interfaces. Keep defining, keep building, and create amazing type-safe applications! ๐Ÿš€


Happy coding! ๐ŸŽ‰๐Ÿš€โœจ