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:
- Consistent Initialization ๐ฏ: Ensure objects start in valid states
- Type Safety ๐ก๏ธ: Catch initialization errors at compile time
- Code Clarity ๐: Make object creation intentions explicit
- Validation โ : Validate data before object creation
- 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
- ๐ฏ Use Parameter Properties: Reduce boilerplate when appropriate
- ๐ก๏ธ Validate Early: Check parameters in constructor
- ๐ Clear Signatures: Use overloading for complex initialization
- ๐ญ Factory Methods: Create semantic constructors for different use cases
- โก Call super() First: Always call parent constructor before using โthisโ
- ๐ 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:
- ๐ป Complete the vehicle management system exercise above
- ๐๏ธ Experiment with different constructor patterns in your projects
- ๐ Next tutorial: Access Modifiers - public, private, protected, and readonly
- ๐ Share your constructor patterns with the community!
Remember: Every great object starts with a well-designed constructor. Keep building, keep learning! ๐ช
Happy coding! ๐๐๏ธโจ