+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 28 of 354

๐Ÿ— ๏ธ Constructors in TypeScript: Initializing Objects

Master TypeScript constructors with parameter properties, overloading, inheritance patterns, and advanced initialization techniques ๐Ÿš€

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Understanding of TypeScript classes ๐Ÿ—๏ธ
  • Basic knowledge of object-oriented programming ๐Ÿ“š
  • Familiarity with function parameters ๐Ÿ”ง

What you'll learn

  • Master constructor syntax and parameter properties ๐ŸŽฏ
  • Implement constructor overloading patterns ๐Ÿ› ๏ธ
  • Handle inheritance with super() calls โœจ
  • Create flexible object initialization systems ๐Ÿš€

๐ŸŽฏ Introduction

Welcome to the world of TypeScript constructors! ๐ŸŽ‰ Think of constructors as the special โ€œbirth certificateโ€ ๐Ÿ“‹ for your objects - they define exactly how each new instance comes to life!

Constructors are like the assembly line workers ๐Ÿญ in a factory. They take raw materials (parameters) and carefully craft them into fully-formed, ready-to-use objects. Whether youโ€™re building a simple User class or a complex DatabaseConnection, constructors ensure everything starts off perfectly! โœจ

By the end of this tutorial, youโ€™ll be a constructor wizard, creating elegant initialization patterns that make your code both powerful and maintainable! ๐Ÿง™โ€โ™‚๏ธ

Letโ€™s build some amazing objects together! ๐Ÿš€

๐Ÿ“š Understanding Constructors

๐Ÿค” What are Constructors?

Constructors are special methods that:

  • ๐Ÿ—๏ธ Initialize new object instances
  • ๐ŸŽฏ Set up initial property values
  • โšก Run setup logic when objects are created
  • ๐Ÿ›ก๏ธ Validate input parameters
  • ๐Ÿ”ง Configure object state

Think of constructors like:

  • ๐Ÿ  House builders: Set up the foundation, walls, and rooms
  • ๐ŸŽ‚ Cake bakers: Mix ingredients to create the perfect cake
  • ๐Ÿš— Car manufacturers: Assemble parts into a working vehicle

๐Ÿ’ก Why Master Constructors?

Hereโ€™s why constructors are essential:

  1. Consistent Initialization ๐ŸŽฏ: Ensure objects start in valid states
  2. Type Safety ๐Ÿ›ก๏ธ: Catch initialization errors at compile time
  3. Code Clarity ๐Ÿ“–: Make object creation intentions explicit
  4. Validation โœ…: Validate data before object creation
  5. Flexibility ๐Ÿคธ: Support multiple initialization patterns

๐Ÿ”ง Basic Constructor Patterns

๐Ÿ“ Simple Constructor

Letโ€™s start with basic constructor syntax:

// ๐ŸŽฎ Game Character with basic constructor
class GameCharacter {
  // ๐Ÿ“Š Properties to be initialized
  name: string;
  level: number;
  health: number;
  class: string;
  
  // ๐Ÿ—๏ธ Constructor - the magic happens here!
  constructor(name: string, characterClass: string) {
    this.name = name;
    this.class = characterClass;
    this.level = 1;           // ๐ŸŒŸ Default starting level
    this.health = 100;        // โค๏ธ Full health to start
    
    console.log(`โš”๏ธ ${name} the ${characterClass} has joined the adventure!`);
  }
  
  // ๐Ÿ“Š Display character info
  getInfo(): string {
    return `๐Ÿ‘ค ${this.name} - Level ${this.level} ${this.class} (${this.health}โค๏ธ)`;
  }
  
  // โšก Level up method
  levelUp(): void {
    this.level++;
    this.health = 100; // ๐Ÿฉน Full heal on level up
    console.log(`๐ŸŽ‰ ${this.name} reached level ${this.level}!`);
  }
}

// ๐Ÿš€ Creating characters
const warrior = new GameCharacter("Thorin", "Warrior");
const mage = new GameCharacter("Gandalf", "Mage");

console.log(warrior.getInfo()); // ๐Ÿ‘ค Thorin - Level 1 Warrior (100โค๏ธ)
console.log(mage.getInfo());    // ๐Ÿ‘ค Gandalf - Level 1 Mage (100โค๏ธ)

โšก Parameter Properties (Constructor Shorthand)

TypeScriptโ€™s parameter properties let you declare and initialize in one line:

// ๐Ÿ›๏ธ Product class with parameter properties
class Product {
  // โœจ Parameter properties - declaration and initialization combined!
  constructor(
    public readonly id: string,           // ๐Ÿ”’ Readonly public property
    public name: string,                  // โœ… Public mutable property
    private _price: number,               // ๐Ÿ”’ Private property
    protected category: string,           // ๐Ÿ‘ฅ Protected property
    public description: string = "",      // ๐ŸŽฏ Default value
    public inStock: boolean = true        // ๐Ÿ“ฆ Default stock status
  ) {
    // ๐Ÿ›ก๏ธ Validation in constructor body
    if (_price < 0) {
      throw new Error("โŒ Price cannot be negative");
    }
    
    if (name.trim().length === 0) {
      throw new Error("โŒ Product name cannot be empty");
    }
    
    console.log(`๐Ÿ“ฆ Product "${name}" created successfully!`);
  }
  
  // ๐Ÿ’ฐ Price getter with formatting
  get price(): string {
    return `$${this._price.toFixed(2)}`;
  }
  
  // ๐Ÿ’ฐ Price setter with validation
  set price(value: number) {
    if (value < 0) {
      throw new Error("โŒ Price cannot be negative");
    }
    this._price = value;
    console.log(`๐Ÿ’ฐ Price updated to $${value.toFixed(2)}`);
  }
  
  // ๐Ÿ“Š Product summary
  getSummary(): string {
    const stock = this.inStock ? "โœ… In Stock" : "โŒ Out of Stock";
    return `๐Ÿ›๏ธ ${this.name} - ${this.price} (${this.category}) ${stock}`;
  }
  
  // ๐Ÿ”„ Update stock status
  updateStock(inStock: boolean): void {
    this.inStock = inStock;
    const status = inStock ? "restocked" : "sold out";
    console.log(`๐Ÿ“ฆ ${this.name} is now ${status}`);
  }
}

// ๐Ÿš€ Create products with different initialization patterns
const laptop = new Product(
  "LAPTOP001", 
  "Gaming Laptop", 
  2499.99, 
  "Electronics",
  "High-performance gaming laptop with RGB lighting"
);

const book = new Product("BOOK001", "TypeScript Guide", 39.99, "Books");

const coffee = new Product(
  "COFFEE001", 
  "Premium Coffee", 
  12.99, 
  "Beverages",
  "Artisan roasted coffee beans",
  false  // Out of stock
);

console.log(laptop.getSummary());
console.log(book.getSummary());
console.log(coffee.getSummary());

๐Ÿ’ก Advanced Constructor Patterns

๐ŸŽฏ Constructor Overloading

Create flexible constructors with multiple signatures:

// ๐Ÿ“… Event class with constructor overloading
class Event {
  public readonly id: string;
  public title: string;
  public date: Date;
  public location: string;
  public description: string;
  public isVirtual: boolean;
  
  // ๐ŸŽฏ Constructor overload signatures
  constructor(title: string, date: Date);
  constructor(title: string, date: Date, location: string);
  constructor(title: string, date: Date, location: string, description: string);
  constructor(id: string, title: string, date: Date, location: string, description: string, isVirtual: boolean);
  
  // ๐Ÿ—๏ธ Constructor implementation (handles all cases)
  constructor(
    titleOrId: string,
    dateOrTitle?: Date | string,
    locationOrDate?: string | Date,
    descriptionOrLocation?: string,
    isVirtualOrDescription?: boolean | string,
    actualIsVirtual?: boolean
  ) {
    // ๐Ÿ” Determine which overload was used
    if (arguments.length >= 6) {
      // Full constructor with ID
      this.id = titleOrId;
      this.title = dateOrTitle as string;
      this.date = locationOrDate as Date;
      this.location = descriptionOrLocation!;
      this.description = isVirtualOrDescription as string;
      this.isVirtual = actualIsVirtual!;
    } else {
      // Simplified constructors
      this.id = `evt_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
      this.title = titleOrId;
      this.date = dateOrTitle as Date;
      this.location = (locationOrDate as string) || "TBD";
      this.description = (descriptionOrLocation as string) || "";
      this.isVirtual = false;
    }
    
    console.log(`๐Ÿ“… Event "${this.title}" scheduled for ${this.date.toDateString()}`);
  }
  
  // ๐Ÿ“Š Event details
  getEventDetails(): string {
    const venue = this.isVirtual ? "๐ŸŒ Virtual Event" : `๐Ÿ“ ${this.location}`;
    const desc = this.description ? `\n๐Ÿ“ ${this.description}` : "";
    
    return `
๐Ÿ“… ${this.title}
๐Ÿ—“๏ธ ${this.date.toDateString()} at ${this.date.toLocaleTimeString()}
${venue}${desc}
๐Ÿ†” Event ID: ${this.id}
    `.trim();
  }
  
  // โฐ Time until event
  getTimeUntilEvent(): string {
    const now = new Date();
    const timeDiff = this.date.getTime() - now.getTime();
    
    if (timeDiff < 0) return "๐Ÿ“… Event has passed";
    
    const days = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
    const hours = Math.floor((timeDiff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    
    if (days > 0) return `๐Ÿ•’ ${days} days, ${hours} hours remaining`;
    return `๐Ÿ•’ ${hours} hours remaining`;
  }
}

// ๐Ÿš€ Test different constructor overloads
const conference = new Event(
  "TypeScript Conference 2025",
  new Date("2025-09-15T09:00:00")
);

const workshop = new Event(
  "Advanced TypeScript Workshop",
  new Date("2025-07-20T14:00:00"),
  "Tech Hub Downtown"
);

const meetup = new Event(
  "Monthly TypeScript Meetup",
  new Date("2025-06-25T18:30:00"),
  "Community Center",
  "Join us for an evening of TypeScript discussions and networking!"
);

const webinar = new Event(
  "WEBINAR001",
  "TypeScript Best Practices",
  new Date("2025-06-30T16:00:00"),
  "Zoom Meeting Room",
  "Learn advanced TypeScript patterns from industry experts",
  true
);

console.log(conference.getEventDetails());
console.log(workshop.getTimeUntilEvent());
console.log(meetup.getEventDetails());
console.log(webinar.getEventDetails());

๐Ÿญ Factory Constructor Pattern

// ๐Ÿช Store Item with factory methods
class StoreItem {
  constructor(
    public readonly id: string,
    public name: string,
    public price: number,
    public category: string,
    public tags: string[] = [],
    public metadata: Record<string, any> = {}
  ) {
    console.log(`๐Ÿท๏ธ Created ${category} item: ${name}`);
  }
  
  // ๐Ÿ“ฑ Static factory method for electronics
  static createElectronics(
    name: string, 
    price: number, 
    brand: string, 
    model: string,
    warranty: number
  ): StoreItem {
    return new StoreItem(
      `ELEC_${Date.now()}`,
      name,
      price,
      "Electronics",
      ["tech", "gadget", brand.toLowerCase()],
      { brand, model, warranty }
    );
  }
  
  // ๐Ÿ“š Static factory method for books
  static createBook(
    title: string,
    price: number,
    author: string,
    isbn: string,
    pages: number
  ): StoreItem {
    return new StoreItem(
      `BOOK_${Date.now()}`,
      title,
      price,
      "Books",
      ["literature", "reading", author.toLowerCase().replace(/\s+/g, "-")],
      { author, isbn, pages }
    );
  }
  
  // ๐Ÿ‘• Static factory method for clothing
  static createClothing(
    name: string,
    price: number,
    size: string,
    color: string,
    material: string
  ): StoreItem {
    return new StoreItem(
      `CLOTH_${Date.now()}`,
      name,
      price,
      "Clothing",
      ["fashion", "apparel", color.toLowerCase(), size.toLowerCase()],
      { size, color, material }
    );
  }
  
  // ๐Ÿ• Static factory method for food items
  static createFood(
    name: string,
    price: number,
    cuisine: string,
    ingredients: string[],
    calories: number
  ): StoreItem {
    return new StoreItem(
      `FOOD_${Date.now()}`,
      name,
      price,
      "Food",
      ["edible", cuisine.toLowerCase(), ...ingredients.map(i => i.toLowerCase())],
      { cuisine, ingredients, calories }
    );
  }
  
  // ๐Ÿ“Š Get formatted info
  getFormattedInfo(): string {
    const metaInfo = Object.entries(this.metadata)
      .map(([key, value]) => `${key}: ${value}`)
      .join(", ");
    
    return `
๐Ÿท๏ธ ${this.name} (${this.category})
๐Ÿ’ฐ $${this.price.toFixed(2)}
๐Ÿท๏ธ Tags: ${this.tags.join(", ")}
๐Ÿ“‹ Details: ${metaInfo}
๐Ÿ†” ID: ${this.id}
    `.trim();
  }
}

// ๐Ÿš€ Use factory methods for type-safe creation
const smartphone = StoreItem.createElectronics(
  "iPhone 15 Pro", 
  1199.99, 
  "Apple", 
  "A17-PRO", 
  12
);

const novel = StoreItem.createBook(
  "The TypeScript Chronicles",
  24.99,
  "Jane Developer",
  "978-3-16-148410-0",
  324
);

const tshirt = StoreItem.createClothing(
  "TypeScript Developer Tee",
  29.99,
  "L",
  "Blue",
  "100% Cotton"
);

const pizza = StoreItem.createFood(
  "Margherita Pizza",
  18.99,
  "Italian",
  ["tomato sauce", "mozzarella", "basil", "olive oil"],
  650
);

console.log(smartphone.getFormattedInfo());
console.log("\n" + novel.getFormattedInfo());
console.log("\n" + tshirt.getFormattedInfo());
console.log("\n" + pizza.getFormattedInfo());

๐ŸŽญ Inheritance and Super Constructors

๐Ÿ‘ฅ Constructor Inheritance

// ๐Ÿพ Animal base class
class Animal {
  protected energy: number = 100;
  
  constructor(
    public name: string,
    public species: string,
    public age: number,
    protected habitat: string = "Unknown"
  ) {
    console.log(`๐Ÿพ ${species} named ${name} has been born!`);
  }
  
  // ๐Ÿ’ค Rest to restore energy
  rest(): void {
    this.energy = Math.min(100, this.energy + 20);
    console.log(`๐Ÿ˜ด ${this.name} is resting. Energy: ${this.energy}%`);
  }
  
  // ๐Ÿ“Š Basic info
  getInfo(): string {
    return `๐Ÿพ ${this.name} the ${this.species} (${this.age} years old) - Energy: ${this.energy}%`;
  }
}

// ๐Ÿ• Dog class extending Animal
class Dog extends Animal {
  private tricks: string[] = [];
  
  constructor(
    name: string,
    age: number,
    public breed: string,
    public isTrained: boolean = false
  ) {
    // ๐ŸŽฏ Call parent constructor with super()
    super(name, "Dog", age, "Domestic");
    
    if (isTrained) {
      this.tricks = ["sit", "stay", "come"];
      console.log(`๐ŸŽ“ ${name} is already trained!`);
    }
    
    console.log(`๐Ÿ• ${breed} dog ${name} is ready to play!`);
  }
  
  // ๐ŸŽพ Dog-specific method
  bark(): void {
    this.energy -= 5;
    console.log(`๐Ÿ• ${this.name} says: Woof! Woof!`);
  }
  
  // ๐ŸŽช Learn a new trick
  learnTrick(trick: string): void {
    if (!this.tricks.includes(trick)) {
      this.tricks.push(trick);
      this.energy -= 10;
      console.log(`๐ŸŽช ${this.name} learned to ${trick}!`);
    } else {
      console.log(`๐Ÿค” ${this.name} already knows how to ${trick}`);
    }
  }
  
  // ๐ŸŽญ Perform tricks
  performTricks(): void {
    if (this.tricks.length === 0) {
      console.log(`๐Ÿ˜… ${this.name} doesn't know any tricks yet`);
      return;
    }
    
    console.log(`๐ŸŽช ${this.name} is performing tricks:`);
    this.tricks.forEach(trick => {
      console.log(`  โœจ ${trick}`);
      this.energy -= 3;
    });
  }
  
  // ๐Ÿ“Š Override getInfo with dog-specific details
  getInfo(): string {
    const baseInfo = super.getInfo();
    const trickInfo = this.tricks.length > 0 ? ` | Tricks: ${this.tricks.join(", ")}` : " | No tricks learned";
    return `${baseInfo} | Breed: ${this.breed}${trickInfo}`;
  }
}

// ๐Ÿฑ Cat class extending Animal
class Cat extends Animal {
  private lives: number = 9;
  
  constructor(
    name: string,
    age: number,
    public furColor: string,
    public isIndoor: boolean = true
  ) {
    super(name, "Cat", age, isIndoor ? "Indoor" : "Outdoor");
    console.log(`๐Ÿฑ ${furColor} cat ${name} has ${this.lives} lives remaining!`);
  }
  
  // ๐Ÿ˜บ Cat-specific method
  meow(): void {
    this.energy -= 3;
    console.log(`๐Ÿฑ ${this.name} says: Meow!`);
  }
  
  // ๐ŸŽฏ Hunt (outdoor cats only)
  hunt(): void {
    if (!this.isIndoor) {
      this.energy -= 15;
      console.log(`๐ŸŽฏ ${this.name} is hunting outside!`);
    } else {
      console.log(`๐Ÿ  ${this.name} is an indoor cat and doesn't hunt`);
    }
  }
  
  // ๐Ÿ’ฅ Lose a life
  loseLife(): void {
    if (this.lives > 1) {
      this.lives--;
      console.log(`๐Ÿ’” ${this.name} lost a life! ${this.lives} lives remaining`);
    } else {
      console.log(`๐Ÿ˜ฟ ${this.name} has used all their lives`);
    }
  }
  
  // ๐Ÿ“Š Override getInfo
  getInfo(): string {
    const baseInfo = super.getInfo();
    return `${baseInfo} | Color: ${this.furColor} | Lives: ${this.lives} | ${this.isIndoor ? "Indoor" : "Outdoor"}`;
  }
}

// ๐Ÿš€ Create animals with inheritance
const buddy = new Dog("Buddy", 3, "Golden Retriever", true);
const whiskers = new Cat("Whiskers", 2, "Orange", false);

console.log("\n๐Ÿ“Š Animal Information:");
console.log(buddy.getInfo());
console.log(whiskers.getInfo());

console.log("\n๐ŸŽช Animal Actions:");
buddy.bark();
buddy.learnTrick("roll over");
buddy.performTricks();

whiskers.meow();
whiskers.hunt();
whiskers.loseLife();

console.log("\n๐Ÿ“Š Updated Information:");
console.log(buddy.getInfo());
console.log(whiskers.getInfo());

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Forgetting super() in inheritance

// โŒ Problematic - missing super() call
class BadChild extends Animal {
  constructor(name: string) {
    // โŒ Error! Must call super() before using 'this'
    this.name = name; // โŒ ReferenceError
  }
}

// โœ… Correct - proper super() call
class GoodChild extends Animal {
  constructor(name: string, customProperty: string) {
    super(name, "Unknown", 0); // โœ… Call super() first
    this.customProperty = customProperty; // โœ… Now we can use 'this'
  }
  
  private customProperty: string;
}

๐Ÿคฏ Pitfall 2: Not validating constructor parameters

// โŒ Bad - no validation
class BadUser {
  constructor(
    public email: string,
    public age: number
  ) {
    // No validation - dangerous!
  }
}

// โœ… Good - proper validation
class GoodUser {
  constructor(
    public email: string,
    public age: number
  ) {
    if (!email.includes("@")) {
      throw new Error("โŒ Invalid email format");
    }
    
    if (age < 0 || age > 150) {
      throw new Error("โŒ Age must be between 0 and 150");
    }
    
    console.log(`โœ… User ${email} created successfully`);
  }
}

๐Ÿ”’ Pitfall 3: Async operations in constructors

// โŒ Problematic - async in constructor
class BadAsyncClass {
  constructor(id: string) {
    // โŒ Can't use async/await in constructor
    this.loadData(id); // Might not complete before object is used
  }
  
  private async loadData(id: string) {
    // Async operation
  }
}

// โœ… Better - factory pattern with async
class GoodAsyncClass {
  private constructor(private data: any) {}
  
  static async create(id: string): Promise<GoodAsyncClass> {
    const data = await this.loadData(id);
    return new GoodAsyncClass(data);
  }
  
  private static async loadData(id: string): Promise<any> {
    // Async operation here
    return { id, loaded: true };
  }
}

// Usage:
// const instance = await GoodAsyncClass.create("123");

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use Parameter Properties: Reduce boilerplate when appropriate
  2. ๐Ÿ›ก๏ธ Validate Early: Check parameters in constructor
  3. ๐Ÿ“ Clear Signatures: Use overloading for complex initialization
  4. ๐Ÿญ Factory Methods: Create semantic constructors for different use cases
  5. โšก Call super() First: Always call parent constructor before using โ€˜thisโ€™
  6. ๐Ÿ”’ Readonly When Possible: Use readonly for immutable properties

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Vehicle Management System

Create a comprehensive vehicle system with inheritance and advanced constructors:

๐Ÿ“‹ Requirements:

  • ๐Ÿš— Base Vehicle class with common properties
  • ๐ŸŽ๏ธ Car, Motorcycle, and Truck subclasses
  • ๐Ÿญ Factory methods for different vehicle types
  • ๐Ÿ”ง Constructor overloading for flexible creation
  • ๐Ÿ“Š Vehicle registry with search capabilities
  • โ›ฝ Fuel and maintenance tracking

๐Ÿš€ Bonus Features:

  • Electric vehicle support with battery management
  • Rental system with availability tracking
  • Insurance and registration management
  • Performance analytics and reporting

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐Ÿš— Base Vehicle class
abstract class Vehicle {
  protected static nextVehicleId: number = 1;
  public readonly id: string;
  protected mileage: number = 0;
  protected fuelLevel: number = 100;
  protected maintenanceHistory: string[] = [];
  
  constructor(
    public readonly make: string,
    public readonly model: string,
    public readonly year: number,
    public readonly vin: string,
    protected fuelCapacity: number,
    protected fuelType: "gasoline" | "diesel" | "electric" | "hybrid"
  ) {
    this.id = `VEH${Vehicle.nextVehicleId++}`;
    
    if (year < 1900 || year > new Date().getFullYear() + 1) {
      throw new Error("โŒ Invalid vehicle year");
    }
    
    if (vin.length !== 17) {
      throw new Error("โŒ VIN must be 17 characters");
    }
    
    console.log(`๐Ÿš— ${year} ${make} ${model} registered with ID: ${this.id}`);
  }
  
  // Abstract methods for subclasses
  abstract getVehicleType(): string;
  abstract getMaxSpeed(): number;
  abstract calculateMaintenanceCost(): number;
  
  // ๐Ÿ›ฃ๏ธ Drive the vehicle
  drive(distance: number): void {
    if (distance <= 0) {
      console.log("โŒ Distance must be positive");
      return;
    }
    
    const fuelNeeded = this.calculateFuelConsumption(distance);
    
    if (fuelNeeded > this.fuelLevel) {
      console.log(`โ›ฝ Not enough fuel! Need ${fuelNeeded.toFixed(1)}%, have ${this.fuelLevel.toFixed(1)}%`);
      return;
    }
    
    this.mileage += distance;
    this.fuelLevel -= fuelNeeded;
    
    console.log(`๐Ÿ›ฃ๏ธ Drove ${distance} miles. Fuel: ${this.fuelLevel.toFixed(1)}%, Mileage: ${this.mileage}`);
    
    // Check if maintenance is needed
    if (this.mileage % 5000 < distance) {
      console.log(`๐Ÿ”ง ${this.make} ${this.model} needs maintenance!`);
    }
  }
  
  // โ›ฝ Refuel the vehicle
  refuel(amount?: number): void {
    const maxRefuel = 100 - this.fuelLevel;
    const actualRefuel = amount ? Math.min(amount, maxRefuel) : maxRefuel;
    
    this.fuelLevel += actualRefuel;
    console.log(`โ›ฝ Refueled ${actualRefuel.toFixed(1)}%. Current level: ${this.fuelLevel.toFixed(1)}%`);
  }
  
  // ๐Ÿ”ง Perform maintenance
  performMaintenance(type: string, cost: number): void {
    const maintenanceRecord = `${new Date().toISOString().split('T')[0]}: ${type} ($${cost})`;
    this.maintenanceHistory.push(maintenanceRecord);
    console.log(`๐Ÿ”ง Maintenance performed: ${type}`);
  }
  
  // ๐Ÿ“Š Vehicle information
  getInfo(): string {
    return `
๐Ÿš— ${this.year} ${this.make} ${this.model}
๐Ÿ†” ID: ${this.id} | VIN: ${this.vin}
๐Ÿ“Š Type: ${this.getVehicleType()}
๐Ÿ›ฃ๏ธ Mileage: ${this.mileage} miles
โ›ฝ Fuel: ${this.fuelLevel.toFixed(1)}% (${this.fuelType})
๐ŸŽ๏ธ Max Speed: ${this.getMaxSpeed()} mph
๐Ÿ’ฐ Est. Maintenance Cost: $${this.calculateMaintenanceCost()}
    `.trim();
  }
  
  // Calculate fuel consumption based on vehicle type
  protected abstract calculateFuelConsumption(distance: number): number;
}

// ๐ŸŽ๏ธ Car class
class Car extends Vehicle {
  constructor(
    make: string,
    model: string,
    year: number,
    vin: string,
    public readonly doors: 2 | 4,
    public readonly transmission: "manual" | "automatic",
    fuelType: "gasoline" | "electric" | "hybrid" = "gasoline"
  ) {
    super(make, model, year, vin, fuelType === "electric" ? 100 : 15, fuelType);
    console.log(`๐Ÿš™ ${doors}-door ${transmission} car ready for the road!`);
  }
  
  // ๐Ÿญ Factory method for electric car
  static createElectric(
    make: string,
    model: string,
    year: number,
    vin: string,
    batteryCapacity: number,
    doors: 2 | 4 = 4
  ): Car {
    const car = new Car(make, model, year, vin, doors, "automatic", "electric");
    car.fuelCapacity = batteryCapacity;
    console.log(`โšก Electric car with ${batteryCapacity}kWh battery created`);
    return car;
  }
  
  // ๐Ÿญ Factory method for sports car
  static createSportsCar(
    make: string,
    model: string,
    year: number,
    vin: string,
    transmission: "manual" | "automatic" = "manual"
  ): Car {
    return new Car(make, model, year, vin, 2, transmission, "gasoline");
  }
  
  getVehicleType(): string {
    return `${this.doors}-door Car`;
  }
  
  getMaxSpeed(): number {
    // Sports cars (2-door) are faster
    const baseSpeed = this.doors === 2 ? 180 : 150;
    return this.fuelType === "electric" ? baseSpeed + 20 : baseSpeed;
  }
  
  calculateMaintenanceCost(): number {
    const baseCost = 500;
    const ageFactor = (new Date().getFullYear() - this.year) * 50;
    const mileageFactor = Math.floor(this.mileage / 10000) * 100;
    return baseCost + ageFactor + mileageFactor;
  }
  
  protected calculateFuelConsumption(distance: number): number {
    if (this.fuelType === "electric") {
      return distance * 0.3; // Electric cars are more efficient
    }
    const efficiency = this.doors === 2 ? 0.8 : 0.6; // Sports cars use more fuel
    return distance * efficiency;
  }
  
  // ๐ŸŽต Car-specific feature
  playMusic(): void {
    console.log(`๐ŸŽต Playing music in the ${this.make} ${this.model}!`);
  }
}

// ๐Ÿ๏ธ Motorcycle class
class Motorcycle extends Vehicle {
  constructor(
    make: string,
    model: string,
    year: number,
    vin: string,
    public readonly engineSize: number, // in cc
    public readonly hasWindshield: boolean = false
  ) {
    super(make, model, year, vin, 4, "gasoline");
    
    if (engineSize < 50 || engineSize > 2000) {
      throw new Error("โŒ Engine size must be between 50cc and 2000cc");
    }
    
    console.log(`๐Ÿ๏ธ ${engineSize}cc motorcycle ready to ride!`);
  }
  
  // ๐Ÿญ Factory method for different motorcycle types
  static createSportBike(make: string, model: string, year: number, vin: string): Motorcycle {
    return new Motorcycle(make, model, year, vin, 1000, false);
  }
  
  static createTouring(make: string, model: string, year: number, vin: string): Motorcycle {
    return new Motorcycle(make, model, year, vin, 1200, true);
  }
  
  static createCruiser(make: string, model: string, year: number, vin: string): Motorcycle {
    return new Motorcycle(make, model, year, vin, 1600, false);
  }
  
  getVehicleType(): string {
    if (this.engineSize >= 1000) return "Sport Motorcycle";
    if (this.engineSize >= 600) return "Standard Motorcycle";
    return "Small Motorcycle";
  }
  
  getMaxSpeed(): number {
    return Math.min(200, 80 + (this.engineSize / 10));
  }
  
  calculateMaintenanceCost(): number {
    const baseCost = 300;
    const engineFactor = Math.floor(this.engineSize / 100) * 25;
    const ageFactor = (new Date().getFullYear() - this.year) * 30;
    return baseCost + engineFactor + ageFactor;
  }
  
  protected calculateFuelConsumption(distance: number): number {
    const efficiency = 0.3 + (this.engineSize / 5000); // Larger engines use more fuel
    return distance * efficiency;
  }
  
  // ๐Ÿ๏ธ Motorcycle-specific feature
  wheelie(): void {
    if (this.engineSize >= 600) {
      console.log(`๐Ÿคธโ€โ™‚๏ธ ${this.make} ${this.model} is doing a wheelie!`);
    } else {
      console.log(`๐Ÿ˜… ${this.make} ${this.model} doesn't have enough power for a wheelie`);
    }
  }
}

// ๐Ÿš› Truck class
class Truck extends Vehicle {
  private cargo: string[] = [];
  
  constructor(
    make: string,
    model: string,
    year: number,
    vin: string,
    public readonly maxPayload: number, // in pounds
    public readonly bedLength: number, // in feet
    public readonly is4WD: boolean = false
  ) {
    super(make, model, year, vin, 25, "diesel");
    
    if (maxPayload < 1000 || maxPayload > 40000) {
      throw new Error("โŒ Max payload must be between 1,000 and 40,000 pounds");
    }
    
    console.log(`๐Ÿš› ${maxPayload}lb capacity truck with ${bedLength}ft bed ready for work!`);
  }
  
  // ๐Ÿญ Factory methods for different truck types
  static createPickup(make: string, model: string, year: number, vin: string): Truck {
    return new Truck(make, model, year, vin, 2000, 6, true);
  }
  
  static createDelivery(make: string, model: string, year: number, vin: string): Truck {
    return new Truck(make, model, year, vin, 10000, 12, false);
  }
  
  static createHeavyDuty(make: string, model: string, year: number, vin: string): Truck {
    return new Truck(make, model, year, vin, 35000, 20, true);
  }
  
  getVehicleType(): string {
    if (this.maxPayload >= 20000) return "Heavy Duty Truck";
    if (this.maxPayload >= 8000) return "Medium Duty Truck";
    return "Light Duty Truck";
  }
  
  getMaxSpeed(): number {
    // Heavier trucks are slower
    const baseSpeed = 100;
    const payloadPenalty = Math.floor(this.maxPayload / 5000) * 5;
    return Math.max(65, baseSpeed - payloadPenalty);
  }
  
  calculateMaintenanceCost(): number {
    const baseCost = 800;
    const payloadFactor = Math.floor(this.maxPayload / 1000) * 20;
    const ageFactor = (new Date().getFullYear() - this.year) * 100;
    return baseCost + payloadFactor + ageFactor;
  }
  
  protected calculateFuelConsumption(distance: number): number {
    const baseFuel = 1.2; // Trucks use more fuel
    const payloadFactor = 1 + (this.maxPayload / 50000);
    const cargoFactor = 1 + (this.cargo.length * 0.1);
    return distance * baseFuel * payloadFactor * cargoFactor;
  }
  
  // ๐Ÿ“ฆ Load cargo
  loadCargo(item: string): void {
    this.cargo.push(item);
    console.log(`๐Ÿ“ฆ Loaded: ${item}. Total cargo items: ${this.cargo.length}`);
  }
  
  // ๐Ÿ“ค Unload cargo
  unloadCargo(): string[] {
    const unloaded = [...this.cargo];
    this.cargo = [];
    console.log(`๐Ÿ“ค Unloaded ${unloaded.length} items`);
    return unloaded;
  }
  
  // ๐Ÿ“‹ List cargo
  listCargo(): void {
    console.log(`๐Ÿ“‹ Cargo (${this.cargo.length} items):`);
    this.cargo.forEach((item, index) => {
      console.log(`  ${index + 1}. ${item}`);
    });
  }
}

// ๐Ÿข Vehicle Registry
class VehicleRegistry {
  private vehicles: Map<string, Vehicle> = new Map();
  
  // ๐Ÿ“ Register a vehicle
  registerVehicle(vehicle: Vehicle): void {
    this.vehicles.set(vehicle.id, vehicle);
    console.log(`๐Ÿ“ Vehicle ${vehicle.id} registered in the system`);
  }
  
  // ๐Ÿ” Find vehicle by ID
  findVehicle(id: string): Vehicle | undefined {
    return this.vehicles.get(id);
  }
  
  // ๐Ÿ” Search vehicles
  searchVehicles(criteria: {
    make?: string;
    model?: string;
    year?: number;
    type?: string;
  }): Vehicle[] {
    return Array.from(this.vehicles.values()).filter(vehicle => {
      if (criteria.make && !vehicle.make.toLowerCase().includes(criteria.make.toLowerCase())) {
        return false;
      }
      if (criteria.model && !vehicle.model.toLowerCase().includes(criteria.model.toLowerCase())) {
        return false;
      }
      if (criteria.year && vehicle.year !== criteria.year) {
        return false;
      }
      if (criteria.type && !vehicle.getVehicleType().toLowerCase().includes(criteria.type.toLowerCase())) {
        return false;
      }
      return true;
    });
  }
  
  // ๐Ÿ“Š Registry statistics
  getStatistics(): {
    totalVehicles: number;
    byType: Record<string, number>;
    averageYear: number;
  } {
    const vehicles = Array.from(this.vehicles.values());
    const totalVehicles = vehicles.length;
    
    const byType: Record<string, number> = {};
    let totalYear = 0;
    
    vehicles.forEach(vehicle => {
      const type = vehicle.getVehicleType();
      byType[type] = (byType[type] || 0) + 1;
      totalYear += vehicle.year;
    });
    
    const averageYear = totalVehicles > 0 ? Math.round(totalYear / totalVehicles) : 0;
    
    return { totalVehicles, byType, averageYear };
  }
  
  // ๐Ÿ“‹ List all vehicles
  listAllVehicles(): void {
    console.log(`\n๐Ÿš— Vehicle Registry (${this.vehicles.size} vehicles):`);
    console.log('='.repeat(60));
    
    Array.from(this.vehicles.values()).forEach(vehicle => {
      console.log(`${vehicle.id}: ${vehicle.year} ${vehicle.make} ${vehicle.model} (${vehicle.getVehicleType()})`);
    });
  }
}

// ๐Ÿš€ Demo the vehicle management system
const registry = new VehicleRegistry();

console.log("๐Ÿญ Creating vehicles...\n");

// Create different types of vehicles
const sportscar = Car.createSportsCar("Ferrari", "488 GTB", 2023, "1HGBH41JXMN109186");
const electricCar = Car.createElectric("Tesla", "Model S", 2024, "5YJ3E1EA4JF000001", 100);
const pickup = Truck.createPickup("Ford", "F-150", 2023, "1FTFW1ET1DFC10312");
const sportbike = Motorcycle.createSportBike("Yamaha", "R1", 2024, "JYARN23E24A000001");

// Register vehicles
registry.registerVehicle(sportscar);
registry.registerVehicle(electricCar);
registry.registerVehicle(pickup);
registry.registerVehicle(sportbike);

console.log("\n๐Ÿš— Vehicle Information:");
console.log(sportscar.getInfo());
console.log("\n" + electricCar.getInfo());

console.log("\n๐Ÿ›ฃ๏ธ Test driving vehicles:");
sportscar.drive(100);
electricCar.drive(150);
pickup.loadCargo("Building Materials");
pickup.drive(50);
sportbike.wheelie();

console.log("\n๐Ÿ” Search Results for 'Ford':");
const fordVehicles = registry.searchVehicles({ make: "Ford" });
fordVehicles.forEach(v => console.log(`Found: ${v.year} ${v.make} ${v.model}`));

console.log("\n๐Ÿ“Š Registry Statistics:");
const stats = registry.getStatistics();
console.log(`Total Vehicles: ${stats.totalVehicles}`);
console.log(`Average Year: ${stats.averageYear}`);
console.log("Vehicle Types:");
Object.entries(stats.byType).forEach(([type, count]) => {
  console.log(`  ${type}: ${count}`);
});

๐ŸŽ“ Key Takeaways

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

  • โœ… Create flexible constructors with parameter properties โšก
  • โœ… Implement constructor overloading for multiple initialization patterns ๐ŸŽฏ
  • โœ… Handle inheritance properly with super() calls ๐Ÿ‘ฅ
  • โœ… Use factory methods for semantic object creation ๐Ÿญ
  • โœ… Validate constructor parameters for robust initialization ๐Ÿ›ก๏ธ
  • โœ… Build complex object hierarchies with proper inheritance ๐Ÿ—๏ธ

Remember: Good constructors are the foundation of well-designed classes! ๐Ÿš€

๐Ÿค Next Steps

Fantastic progress! ๐ŸŽ‰ Youโ€™re now a constructor expert!

Continue your TypeScript journey with:

  1. ๐Ÿ’ป Complete the vehicle management system exercise above
  2. ๐Ÿ—๏ธ Experiment with different constructor patterns in your projects
  3. ๐Ÿ“š Next tutorial: Access Modifiers - public, private, protected, and readonly
  4. ๐ŸŒŸ Share your constructor patterns with the community!

Remember: Every great object starts with a well-designed constructor. Keep building, keep learning! ๐Ÿ’ช


Happy coding! ๐ŸŽ‰๐Ÿ—๏ธโœจ