Prerequisites
- Class inheritance knowledge ๐๏ธ
- Super keyword understanding ๐
- Method definitions ๐
What you'll learn
- Understand method overriding fundamentals ๐ฏ
- Override methods effectively ๐
- Combine overriding with super calls ๐
- Apply polymorphism patterns ๐ญ
๐ฏ Introduction
Welcome to the powerful world of method overriding! ๐ In this guide, weโll explore how to customize and enhance inherited behavior by replacing parent class methods with your own implementations.
Youโll discover how method overriding is like redecorating a room ๐ - you keep the structure but change how things work inside. Whether youโre building game characters with unique abilities ๐ฎ or payment systems with different processing methods ๐ณ, understanding method overriding is essential for flexible, maintainable code.
By the end of this tutorial, youโll feel confident customizing any inherited behavior! Letโs dive in! ๐โโ๏ธ
๐ Understanding Method Overriding
๐ค What is Method Overriding?
Method overriding is like giving a child class the ability to say โIโll do this my own way!โ ๐โโ๏ธ Think of it as customizing a recipe - you keep the recipe name but change the ingredients and steps.
In TypeScript terms, method overriding allows a child class to provide a specific implementation of a method thatโs already defined in its parent class. This means you can:
- โจ Customize behavior for specific types
- ๐ Implement specialized functionality
- ๐ก๏ธ Maintain consistent interfaces
- ๐ง Create polymorphic behavior
๐ก Why Use Method Overriding?
Hereโs why developers love method overriding:
- Customization ๐จ: Each subclass can behave differently
- Polymorphism ๐ญ: Same method name, different behaviors
- Flexibility ๐คธโโ๏ธ: Change behavior without changing interface
- Specialization ๐ฏ: Fine-tune functionality for specific cases
Real-world example: Imagine a drawing application ๐จ. Your base Shape
class has a draw()
method, but Circle
, Rectangle
, and Triangle
each override it to draw themselves differently.
๐ง Basic Syntax and Usage
๐ Simple Example
Letโs start with a friendly example:
// ๐ต Base music player
class MusicPlayer {
currentSong: string = '';
volume: number = 50;
// ๐ต Basic play method
play(song: string): void {
this.currentSong = song;
console.log(`๐ต Playing: "${song}" at volume ${this.volume}`);
}
// โน๏ธ Basic stop method
stop(): void {
console.log(`โน๏ธ Stopped playing: "${this.currentSong}"`);
this.currentSong = '';
}
// ๐ Volume control
setVolume(volume: number): void {
this.volume = Math.max(0, Math.min(100, volume));
console.log(`๐ Volume set to: ${this.volume}`);
}
// ๐ Player status
getStatus(): string {
return this.currentSong
? `โถ๏ธ Playing: "${this.currentSong}" | ๐ ${this.volume}%`
: 'โน๏ธ Stopped';
}
}
// ๐ง Premium player with enhanced features
class PremiumMusicPlayer extends MusicPlayer {
private equalizer: string = 'flat';
private playlistMode: boolean = false;
// ๐ Override play method with premium features
play(song: string): void {
// ๐ต Custom premium play logic
this.currentSong = song;
console.log(`๐ง Premium Playing: "${song}"`);
console.log(`๐๏ธ Equalizer: ${this.equalizer} | ๐ Volume: ${this.volume}`);
if (this.playlistMode) {
console.log('๐ Playlist mode active - next song queued');
}
}
// ๐ Override stop method with premium features
stop(): void {
if (this.currentSong) {
console.log(`โธ๏ธ Premium Stop: Saving position for "${this.currentSong}"`);
console.log('๐พ Progress saved for resume later');
}
this.currentSong = '';
console.log('โน๏ธ Premium player stopped');
}
// ๐๏ธ Premium-specific method
setEqualizer(preset: string): void {
this.equalizer = preset;
console.log(`๐๏ธ Equalizer set to: ${preset}`);
}
// ๐ Toggle playlist mode
togglePlaylistMode(): void {
this.playlistMode = !this.playlistMode;
console.log(`๐ Playlist mode: ${this.playlistMode ? 'ON' : 'OFF'}`);
}
// ๐ Override status with premium info
getStatus(): string {
const baseStatus = this.currentSong
? `โถ๏ธ Premium Playing: "${this.currentSong}"`
: 'โน๏ธ Premium Stopped';
return `${baseStatus} | ๐๏ธ ${this.equalizer} | ๐ ${this.playlistMode ? 'Playlist' : 'Single'} | ๐ ${this.volume}%`;
}
}
// ๐ฎ Let's test the overriding!
const basicPlayer = new MusicPlayer();
const premiumPlayer = new PremiumMusicPlayer();
console.log('=== Basic Player ===');
basicPlayer.play('TypeScript Blues');
console.log(basicPlayer.getStatus());
basicPlayer.stop();
console.log('\n=== Premium Player ===');
premiumPlayer.setEqualizer('rock');
premiumPlayer.togglePlaylistMode();
premiumPlayer.play('Advanced TypeScript Symphony'); // Different behavior!
console.log(premiumPlayer.getStatus());
premiumPlayer.stop(); // Different behavior!
๐ก Explanation: Notice how the PremiumMusicPlayer
overrides play()
, stop()
, and getStatus()
methods to provide enhanced functionality while keeping the same method signatures!
๐ฏ Overriding with Super Calls
Sometimes you want to enhance rather than completely replace parent behavior:
// ๐ช Base online store
class OnlineStore {
items: Map<string, number> = new Map(); // item -> price
discountRate: number = 0;
// ๐ Add item to store
addItem(name: string, price: number): void {
this.items.set(name, price);
console.log(`โ Added "${name}" for $${price}`);
}
// ๐ฐ Calculate total price
calculateTotal(itemsToBuy: string[]): number {
let total = 0;
for (const item of itemsToBuy) {
const price = this.items.get(item);
if (price) {
total += price;
} else {
console.log(`โ Item "${item}" not found`);
}
}
const discount = total * this.discountRate;
const finalTotal = total - discount;
console.log(`๐ฐ Subtotal: $${total.toFixed(2)}`);
if (discount > 0) {
console.log(`๐ Discount: -$${discount.toFixed(2)}`);
}
console.log(`๐ณ Total: $${finalTotal.toFixed(2)}`);
return finalTotal;
}
// ๐ฏ Apply discount
setDiscount(rate: number): void {
this.discountRate = Math.max(0, Math.min(1, rate));
console.log(`๐ Discount rate set to: ${(this.discountRate * 100).toFixed(1)}%`);
}
}
// ๐ Premium store with loyalty rewards
class PremiumStore extends OnlineStore {
loyaltyPoints: number = 0;
membershipLevel: 'bronze' | 'silver' | 'gold' = 'bronze';
// ๐ Override addItem to include loyalty points
addItem(name: string, price: number): void {
// ๐ Do the basic item addition
super.addItem(name, price);
// โจ Add premium features
const pointsEarned = Math.floor(price / 10); // 1 point per $10
this.loyaltyPoints += pointsEarned;
console.log(`โญ Earned ${pointsEarned} loyalty points! Total: ${this.loyaltyPoints}`);
}
// ๐ Override calculateTotal with membership benefits
calculateTotal(itemsToBy: string[]): number {
// ๐ Apply membership discount first
const membershipDiscounts = {
bronze: 0.05, // 5%
silver: 0.10, // 10%
gold: 0.15 // 15%
};
const originalDiscount = this.discountRate;
const membershipDiscount = membershipDiscounts[this.membershipLevel];
// ๐ฏ Combine discounts (don't stack multiplicatively)
const totalDiscount = Math.min(0.5, originalDiscount + membershipDiscount);
this.discountRate = totalDiscount;
console.log(`๐ Membership level: ${this.membershipLevel.toUpperCase()}`);
console.log(`๐ Total discount rate: ${(totalDiscount * 100).toFixed(1)}%`);
// ๐ Calculate with enhanced discount
const total = super.calculateTotal(itemsToBy);
// โจ Award loyalty points for purchase
const pointsEarned = Math.floor(total / 5); // 1 point per $5 spent
this.loyaltyPoints += pointsEarned;
console.log(`โญ Purchase bonus: +${pointsEarned} loyalty points! Total: ${this.loyaltyPoints}`);
// ๐ Restore original discount for next calculation
this.discountRate = originalDiscount;
return total;
}
// ๐ Upgrade membership based on loyalty points
checkMembershipUpgrade(): void {
let newLevel: 'bronze' | 'silver' | 'gold' = 'bronze';
if (this.loyaltyPoints >= 1000) {
newLevel = 'gold';
} else if (this.loyaltyPoints >= 500) {
newLevel = 'silver';
}
if (newLevel !== this.membershipLevel) {
this.membershipLevel = newLevel;
console.log(`๐ Congratulations! Upgraded to ${newLevel.toUpperCase()} membership!`);
}
}
// ๐ Redeem loyalty points
redeemPoints(points: number): void {
if (points <= this.loyaltyPoints && points >= 100) {
this.loyaltyPoints -= points;
const value = points / 10; // $1 per 10 points
console.log(`๐ Redeemed ${points} points for $${value} credit!`);
} else {
console.log('โ Invalid redemption amount');
}
}
}
// ๐ Let's go shopping!
const premiumStore = new PremiumStore();
// Add some items
premiumStore.addItem('TypeScript Hoodie', 45);
premiumStore.addItem('Programming Mug', 15);
premiumStore.addItem('Mechanical Keyboard', 120);
// Make a purchase
const cart = ['TypeScript Hoodie', 'Programming Mug'];
premiumStore.calculateTotal(cart);
// Check for upgrades
premiumStore.checkMembershipUpgrade();
๐ก Practical Examples
๐ฎ Example 1: RPG Character Combat System
Letโs create a combat system where different character types have unique fighting styles:
// โ๏ธ Base combat character
class CombatCharacter {
name: string;
health: number;
maxHealth: number;
damage: number;
defense: number;
constructor(name: string, health: number, damage: number, defense: number) {
this.name = name;
this.health = health;
this.maxHealth = health;
this.damage = damage;
this.defense = defense;
}
// โ๏ธ Basic attack method - to be overridden
attack(target: CombatCharacter): void {
const actualDamage = Math.max(1, this.damage - target.defense);
target.takeDamage(actualDamage);
console.log(`โ๏ธ ${this.name} attacks ${target.name} for ${actualDamage} damage!`);
}
// ๐ก๏ธ Take damage
takeDamage(damage: number): void {
this.health = Math.max(0, this.health - damage);
console.log(`๐ฅ ${this.name} takes ${damage} damage! Health: ${this.health}/${this.maxHealth}`);
if (this.health === 0) {
console.log(`๐ ${this.name} has been defeated!`);
}
}
// ๐ Heal
heal(amount: number): void {
const oldHealth = this.health;
this.health = Math.min(this.maxHealth, this.health + amount);
const actualHeal = this.health - oldHealth;
console.log(`๐ ${this.name} heals for ${actualHeal} HP! Health: ${this.health}/${this.maxHealth}`);
}
// ๐ Character status
getStatus(): string {
const healthPercent = Math.round((this.health / this.maxHealth) * 100);
return `${this.name}: ${this.health}/${this.maxHealth} HP (${healthPercent}%)`;
}
// ๐ Check if defeated
isDefeated(): boolean {
return this.health <= 0;
}
}
// ๐งโโ๏ธ Wizard with magical attacks
class Wizard extends CombatCharacter {
mana: number;
maxMana: number;
spellPower: number;
constructor(name: string) {
super(name, 80, 25, 5); // Lower HP, higher damage, low defense
this.mana = 100;
this.maxMana = 100;
this.spellPower = 30;
}
// ๐ Override attack with magical spells
attack(target: CombatCharacter): void {
if (this.mana < 20) {
// ๐ Mana too low, use basic staff hit
console.log(`๐ช ${this.name} is out of mana! Using staff attack...`);
super.attack(target); // Use basic attack
return;
}
// โจ Cast spell
this.mana -= 20;
const spellDamage = this.spellPower + Math.floor(Math.random() * 15);
const actualDamage = Math.max(spellDamage, spellDamage - Math.floor(target.defense / 2)); // Magic partially ignores armor
target.takeDamage(actualDamage);
console.log(`โจ ${this.name} casts a spell at ${target.name} for ${actualDamage} magical damage!`);
console.log(`๐ฎ Mana: ${this.mana}/${this.maxMana}`);
}
// ๐งโโ๏ธ Restore mana
meditate(): void {
const restored = Math.min(30, this.maxMana - this.mana);
this.mana += restored;
console.log(`๐งโโ๏ธ ${this.name} meditates and restores ${restored} mana. Current: ${this.mana}/${this.maxMana}`);
}
}
// ๐ก๏ธ Tank with defensive abilities
class Tank extends CombatCharacter {
shieldPoints: number;
maxShieldPoints: number;
constructor(name: string) {
super(name, 150, 15, 20); // High HP, low damage, high defense
this.shieldPoints = 50;
this.maxShieldPoints = 50;
}
// ๐ Override takeDamage to use shield
takeDamage(damage: number): void {
if (this.shieldPoints > 0) {
const shieldAbsorbed = Math.min(this.shieldPoints, damage);
this.shieldPoints -= shieldAbsorbed;
const remainingDamage = damage - shieldAbsorbed;
console.log(`๐ก๏ธ Shield absorbs ${shieldAbsorbed} damage! Shield: ${this.shieldPoints}/${this.maxShieldPoints}`);
if (remainingDamage > 0) {
super.takeDamage(remainingDamage);
}
} else {
super.takeDamage(damage);
}
}
// ๐ Override attack with shield bash
attack(target: CombatCharacter): void {
// ๐ก๏ธ Shield bash attack
const bashDamage = this.damage + Math.floor(this.shieldPoints / 10);
const actualDamage = Math.max(1, bashDamage - target.defense);
target.takeDamage(actualDamage);
console.log(`๐ก๏ธ ${this.name} shield bashes ${target.name} for ${actualDamage} damage!`);
// ๐ช Chance to stun (simplified)
if (Math.random() < 0.3) {
console.log(`๐ต ${target.name} is stunned by the powerful bash!`);
}
}
// ๐ Enhanced status with shield info
getStatus(): string {
const baseStatus = super.getStatus();
return `${baseStatus} | ๐ก๏ธ Shield: ${this.shieldPoints}/${this.maxShieldPoints}`;
}
// ๐ก๏ธ Repair shield
repairShield(): void {
const repaired = Math.min(20, this.maxShieldPoints - this.shieldPoints);
this.shieldPoints += repaired;
console.log(`๐ง ${this.name} repairs shield for ${repaired} points. Shield: ${this.shieldPoints}/${this.maxShieldPoints}`);
}
}
// ๐โโ๏ธ Rogue with stealth attacks
class Rogue extends CombatCharacter {
stealthMode: boolean = false;
criticalChance: number = 0.3;
constructor(name: string) {
super(name, 100, 35, 10); // Medium HP, high damage, medium defense
}
// ๐ Override attack with stealth and critical hits
attack(target: CombatCharacter): void {
let damage = this.damage;
let isCritical = Math.random() < this.criticalChance;
// ๐ Stealth bonus
if (this.stealthMode) {
damage *= 1.5; // 50% bonus damage
isCritical = true; // Guaranteed crit from stealth
this.stealthMode = false; // Stealth broken after attack
console.log(`๐ก๏ธ ${this.name} strikes from the shadows!`);
}
// โก Critical hit
if (isCritical) {
damage *= 2;
console.log(`๐ฅ CRITICAL HIT!`);
}
const actualDamage = Math.max(1, damage - target.defense);
target.takeDamage(actualDamage);
const attackType = this.stealthMode ? 'sneak attacks' : 'strikes';
console.log(`๐ก๏ธ ${this.name} ${attackType} ${target.name} for ${actualDamage} damage!`);
}
// ๐ค Enter stealth mode
enterStealth(): void {
if (!this.stealthMode) {
this.stealthMode = true;
console.log(`๐ค ${this.name} vanishes into the shadows...`);
} else {
console.log(`${this.name} is already in stealth mode`);
}
}
// ๐ Enhanced status with stealth info
getStatus(): string {
const baseStatus = super.getStatus();
const stealthStatus = this.stealthMode ? ' | ๐ค STEALTH' : '';
return `${baseStatus}${stealthStatus}`;
}
}
// ๐ฎ Epic battle simulation!
const gandalf = new Wizard('Gandalf');
const thorin = new Tank('Thorin');
const legolas = new Rogue('Legolas');
console.log('โ๏ธ BATTLE BEGINS! โ๏ธ\n');
// Display initial status
console.log('๐ Initial Status:');
console.log(gandalf.getStatus());
console.log(thorin.getStatus());
console.log(legolas.getStatus());
console.log('\n๐ฅ Round 1:');
legolas.enterStealth();
legolas.attack(thorin); // Sneak attack!
gandalf.attack(legolas); // Spell attack!
thorin.attack(gandalf); // Shield bash!
console.log('\n๐ After Round 1:');
console.log(gandalf.getStatus());
console.log(thorin.getStatus());
console.log(legolas.getStatus());
console.log('\n๐ง Recovery Phase:');
gandalf.meditate(); // Restore mana
thorin.repairShield(); // Fix shield
legolas.heal(15); // Drink potion
console.log('\n๐ฅ Round 2:');
gandalf.attack(thorin); // Spell vs tank!
thorin.attack(legolas); // Shield bash vs rogue!
legolas.attack(gandalf); // Critical strike chance!
๐ Example 2: Vehicle Control System
Letโs create a vehicle system where different vehicles override driving behavior:
// ๐ Base vehicle class
class Vehicle {
make: string;
model: string;
year: number;
speed: number = 0;
maxSpeed: number;
fuelLevel: number = 100;
isRunning: boolean = false;
constructor(make: string, model: string, year: number, maxSpeed: number) {
this.make = make;
this.model = model;
this.year = year;
this.maxSpeed = maxSpeed;
}
// ๐ Start engine
start(): void {
if (!this.isRunning) {
this.isRunning = true;
console.log(`๐ ${this.getVehicleName()} engine started!`);
} else {
console.log(`${this.getVehicleName()} is already running`);
}
}
// โน๏ธ Stop engine
stop(): void {
if (this.isRunning) {
this.isRunning = false;
this.speed = 0;
console.log(`โน๏ธ ${this.getVehicleName()} engine stopped`);
}
}
// ๐ Accelerate - basic implementation
accelerate(amount: number): void {
if (!this.isRunning) {
console.log('โ Cannot accelerate - engine not running!');
return;
}
if (this.fuelLevel <= 0) {
console.log('โฝ Out of fuel!');
return;
}
const newSpeed = Math.min(this.maxSpeed, this.speed + amount);
const actualAcceleration = newSpeed - this.speed;
this.speed = newSpeed;
// โฝ Consume fuel
this.fuelLevel = Math.max(0, this.fuelLevel - (actualAcceleration * 0.5));
console.log(`๐ ${this.getVehicleName()} accelerated to ${this.speed} mph`);
if (this.speed === this.maxSpeed) {
console.log(`๐ Maximum speed reached!`);
}
}
// ๐ Brake - basic implementation
brake(amount: number): void {
const newSpeed = Math.max(0, this.speed - amount);
const actualDeceleration = this.speed - newSpeed;
this.speed = newSpeed;
console.log(`๐ ${this.getVehicleName()} slowed down to ${this.speed} mph`);
if (this.speed === 0) {
console.log(`๐ Vehicle stopped`);
}
}
// ๐ Vehicle info
getVehicleName(): string {
return `${this.year} ${this.make} ${this.model}`;
}
// ๐ Status display
getStatus(): string {
const status = this.isRunning ? 'Running' : 'Stopped';
return `๐ ${this.getVehicleName()} | ${status} | ${this.speed}/${this.maxSpeed} mph | โฝ ${this.fuelLevel.toFixed(1)}%`;
}
}
// ๐๏ธ Sports car with turbo boost
class SportsCar extends Vehicle {
turboMode: boolean = false;
turboFuelConsumption: number = 2.0;
constructor(make: string, model: string, year: number) {
super(make, model, year, 200); // High max speed
}
// ๐ Override accelerate with turbo boost
accelerate(amount: number): void {
if (!this.isRunning) {
console.log('โ Cannot accelerate - engine not running!');
return;
}
if (this.fuelLevel <= 0) {
console.log('โฝ Out of fuel!');
return;
}
// ๐ Turbo boost
if (this.turboMode) {
amount *= 1.5; // 50% more acceleration
console.log('๐ฅ TURBO BOOST ACTIVATED!');
}
const newSpeed = Math.min(this.maxSpeed, this.speed + amount);
const actualAcceleration = newSpeed - this.speed;
this.speed = newSpeed;
// โฝ Higher fuel consumption in turbo
const fuelConsumption = this.turboMode
? actualAcceleration * this.turboFuelConsumption
: actualAcceleration * 0.5;
this.fuelLevel = Math.max(0, this.fuelLevel - fuelConsumption);
console.log(`๐๏ธ ${this.getVehicleName()} ${this.turboMode ? 'TURBO ' : ''}accelerated to ${this.speed} mph`);
if (this.speed === this.maxSpeed) {
console.log(`๐ Top speed achieved! What a machine! ๐ฅ`);
}
}
// ๐ Override brake with sport brakes
brake(amount: number): void {
// ๐๏ธ Sports cars have better brakes
amount *= 1.3; // 30% better braking
const newSpeed = Math.max(0, this.speed - amount);
console.log(`๐๏ธ ${this.getVehicleName()} sport brakes engaged!`);
super.brake(amount); // Use enhanced braking amount
}
// ๐ Toggle turbo mode
toggleTurbo(): void {
if (this.fuelLevel < 20) {
console.log('โ Insufficient fuel for turbo mode!');
return;
}
this.turboMode = !this.turboMode;
console.log(`๐ฅ Turbo mode: ${this.turboMode ? 'ON' : 'OFF'}`);
}
// ๐ Enhanced status with turbo info
getStatus(): string {
const baseStatus = super.getStatus();
const turboStatus = this.turboMode ? ' | ๐ฅ TURBO' : '';
return `${baseStatus}${turboStatus}`;
}
}
// ๐ Truck with cargo handling
class Truck extends Vehicle {
cargoWeight: number = 0;
maxCargoWeight: number = 5000; // 5000 lbs
trailerAttached: boolean = false;
constructor(make: string, model: string, year: number) {
super(make, model, year, 85); // Lower max speed
}
// ๐ Override accelerate considering cargo weight
accelerate(amount: number): void {
if (!this.isRunning) {
console.log('โ Cannot accelerate - engine not running!');
return;
}
// ๐ฆ Cargo affects acceleration
const cargoFactor = 1 - (this.cargoWeight / this.maxCargoWeight) * 0.6; // Up to 60% reduction
const trailerFactor = this.trailerAttached ? 0.7 : 1.0; // 30% reduction with trailer
const adjustedAmount = amount * cargoFactor * trailerFactor;
if (adjustedAmount < amount * 0.5) {
console.log('๐ Heavy load - slow acceleration');
}
// ๐ Use parent accelerate with adjusted amount
const oldSpeed = this.speed;
super.accelerate(adjustedAmount);
// ๐ฆ Additional fuel consumption for cargo
const cargoFuelPenalty = (this.cargoWeight / 1000) * 0.2;
this.fuelLevel = Math.max(0, this.fuelLevel - cargoFuelPenalty);
console.log(`๐ Hauling ${this.cargoWeight} lbs${this.trailerAttached ? ' with trailer' : ''}`);
}
// ๐ Override brake with cargo considerations
brake(amount: number): void {
// ๐ Heavier loads need more distance to stop
const cargoFactor = 1 - (this.cargoWeight / this.maxCargoWeight) * 0.4; // Reduced braking efficiency
const adjustedAmount = amount * cargoFactor;
if (cargoFactor < 0.8) {
console.log('โ ๏ธ Heavy load - extended braking distance!');
}
super.brake(adjustedAmount);
}
// ๐ฆ Load cargo
loadCargo(weight: number): void {
if (this.cargoWeight + weight <= this.maxCargoWeight) {
this.cargoWeight += weight;
console.log(`๐ฆ Loaded ${weight} lbs. Total cargo: ${this.cargoWeight}/${this.maxCargoWeight} lbs`);
} else {
console.log(`โ Cannot load ${weight} lbs - would exceed capacity!`);
}
}
// ๐ค Unload cargo
unloadCargo(weight: number): void {
if (weight <= this.cargoWeight) {
this.cargoWeight -= weight;
console.log(`๐ค Unloaded ${weight} lbs. Remaining cargo: ${this.cargoWeight} lbs`);
} else {
console.log(`โ Cannot unload ${weight} lbs - only ${this.cargoWeight} lbs loaded`);
}
}
// ๐ Attach/detach trailer
toggleTrailer(): void {
this.trailerAttached = !this.trailerAttached;
console.log(`๐ Trailer ${this.trailerAttached ? 'attached' : 'detached'}`);
}
// ๐ Enhanced status with cargo info
getStatus(): string {
const baseStatus = super.getStatus();
const cargoStatus = ` | ๐ฆ ${this.cargoWeight}/${this.maxCargoWeight} lbs`;
const trailerStatus = this.trailerAttached ? ' | ๐ Trailer' : '';
return `${baseStatus}${cargoStatus}${trailerStatus}`;
}
}
// โก Electric car with battery management
class ElectricCar extends Vehicle {
batteryLevel: number = 100; // Replace fuel with battery
regenerativeBraking: boolean = true;
ecoMode: boolean = false;
constructor(make: string, model: string, year: number) {
super(make, model, year, 120);
this.fuelLevel = 0; // Electric cars don't use fuel
}
// ๐ Override accelerate for electric efficiency
accelerate(amount: number): void {
if (!this.isRunning) {
console.log('โ Cannot accelerate - car not ready!');
return;
}
if (this.batteryLevel <= 0) {
console.log('๐ Battery depleted!');
return;
}
// โก Eco mode affects acceleration
if (this.ecoMode) {
amount *= 0.7; // Reduced acceleration in eco mode
console.log('๐ฑ Eco mode - gentle acceleration');
}
const newSpeed = Math.min(this.maxSpeed, this.speed + amount);
const actualAcceleration = newSpeed - this.speed;
this.speed = newSpeed;
// ๐ Consume battery power
const powerConsumption = this.ecoMode
? actualAcceleration * 0.3
: actualAcceleration * 0.5;
this.batteryLevel = Math.max(0, this.batteryLevel - powerConsumption);
console.log(`โก ${this.getVehicleName()} silently accelerated to ${this.speed} mph`);
}
// ๐ Override brake with regenerative braking
brake(amount: number): void {
const initialSpeed = this.speed;
// ๐ Standard braking
super.brake(amount);
// ๐ Regenerative braking recovers energy
if (this.regenerativeBraking && initialSpeed > this.speed) {
const energyRecovered = (initialSpeed - this.speed) * 0.3;
this.batteryLevel = Math.min(100, this.batteryLevel + energyRecovered);
console.log(`๐ Regenerative braking recovered ${energyRecovered.toFixed(1)}% battery`);
}
}
// ๐ Charge battery
charge(minutes: number): void {
const chargeRate = 2; // 2% per minute
const chargeAmount = minutes * chargeRate;
const oldLevel = this.batteryLevel;
this.batteryLevel = Math.min(100, this.batteryLevel + chargeAmount);
const actualCharge = this.batteryLevel - oldLevel;
console.log(`๐ Charged for ${minutes} minutes. Battery: ${this.batteryLevel.toFixed(1)}% (+${actualCharge.toFixed(1)}%)`);
}
// ๐ฑ Toggle eco mode
toggleEcoMode(): void {
this.ecoMode = !this.ecoMode;
console.log(`๐ฑ Eco mode: ${this.ecoMode ? 'ON' : 'OFF'}`);
}
// ๐ Override status for electric car
getStatus(): string {
const status = this.isRunning ? 'Ready' : 'Parked';
const ecoStatus = this.ecoMode ? ' | ๐ฑ ECO' : '';
const regenStatus = this.regenerativeBraking ? ' | ๐ REGEN' : '';
return `โก ${this.getVehicleName()} | ${status} | ${this.speed}/${this.maxSpeed} mph | ๐ ${this.batteryLevel.toFixed(1)}%${ecoStatus}${regenStatus}`;
}
}
// ๐ Let's test our vehicles!
const ferrari = new SportsCar('Ferrari', '488 GTB', 2023);
const ford = new Truck('Ford', 'F-150', 2023);
const tesla = new ElectricCar('Tesla', 'Model S', 2023);
console.log('๐ VEHICLE TEST DRIVE ๐\n');
// Ferrari test
console.log('=== ๐๏ธ Sports Car Test ===');
ferrari.start();
ferrari.accelerate(50);
ferrari.toggleTurbo();
ferrari.accelerate(30); // Turbo acceleration!
console.log(ferrari.getStatus());
ferrari.brake(40);
console.log(ferrari.getStatus());
console.log('\n=== ๐ Truck Test ===');
ford.start();
ford.loadCargo(3000);
ford.toggleTrailer();
ford.accelerate(30); // Slow due to cargo and trailer
console.log(ford.getStatus());
ford.brake(20); // Extended braking
ford.unloadCargo(1000);
console.log(ford.getStatus());
console.log('\n=== โก Electric Car Test ===');
tesla.start();
tesla.toggleEcoMode();
tesla.accelerate(40); // Eco mode acceleration
console.log(tesla.getStatus());
tesla.brake(25); // Regenerative braking
console.log(tesla.getStatus());
tesla.charge(10); // Quick charge
console.log(tesla.getStatus());
๐ Advanced Concepts
๐งโโ๏ธ Advanced Topic 1: Abstract Method Overriding
Sometimes you want to force child classes to implement specific methods:
// ๐จ Abstract media player
abstract class MediaPlayer {
title: string;
isPlaying: boolean = false;
constructor(title: string) {
this.title = title;
}
// ๐ Concrete method - same for all players
togglePlay(): void {
if (this.isPlaying) {
this.pause();
} else {
this.play();
}
}
// ๐ฏ Abstract methods - MUST be overridden
abstract play(): void;
abstract pause(): void;
abstract getDuration(): number;
abstract getFormat(): string;
}
// ๐ต Audio player implementation
class AudioPlayer extends MediaPlayer {
private audioData: string;
private currentTime: number = 0;
private duration: number;
constructor(title: string, audioData: string, duration: number) {
super(title);
this.audioData = audioData;
this.duration = duration;
}
// โ
Must implement - play method
play(): void {
this.isPlaying = true;
console.log(`๐ต Playing audio: "${this.title}" from ${this.currentTime}s`);
}
// โ
Must implement - pause method
pause(): void {
this.isPlaying = false;
console.log(`โธ๏ธ Paused audio: "${this.title}" at ${this.currentTime}s`);
}
// โ
Must implement - getDuration method
getDuration(): number {
return this.duration;
}
// โ
Must implement - getFormat method
getFormat(): string {
return 'MP3 Audio ๐ต';
}
}
// ๐น Video player implementation
class VideoPlayer extends MediaPlayer {
private videoData: string;
private currentFrame: number = 0;
private totalFrames: number;
private fps: number = 30;
constructor(title: string, videoData: string, totalFrames: number) {
super(title);
this.videoData = videoData;
this.totalFrames = totalFrames;
}
// โ
Must implement - play method
play(): void {
this.isPlaying = true;
console.log(`๐น Playing video: "${this.title}" from frame ${this.currentFrame}`);
}
// โ
Must implement - pause method
pause(): void {
this.isPlaying = false;
console.log(`โธ๏ธ Paused video: "${this.title}" at frame ${this.currentFrame}`);
}
// โ
Must implement - getDuration method
getDuration(): number {
return Math.round(this.totalFrames / this.fps);
}
// โ
Must implement - getFormat method
getFormat(): string {
return `MP4 Video ๐น (${this.fps}fps)`;
}
}
๐๏ธ Advanced Topic 2: Method Overriding with Generics
Combine overriding with generic types for maximum flexibility:
// ๐๏ธ Generic data repository
abstract class Repository<T> {
protected items: T[] = [];
// ๐ Common methods for all repositories
getAll(): T[] {
return [...this.items];
}
count(): number {
return this.items.length;
}
// ๐ฏ Abstract methods to be overridden
abstract add(item: T): void;
abstract findById(id: string | number): T | undefined;
abstract update(id: string | number, updates: Partial<T>): boolean;
abstract delete(id: string | number): boolean;
}
// ๐ค User repository with specific validation
interface User {
id: number;
name: string;
email: string;
age: number;
}
class UserRepository extends Repository<User> {
private nextId: number = 1;
// ๐ Override add with user validation
add(user: Omit<User, 'id'>): void {
// ๐ก๏ธ Validate email format
if (!user.email.includes('@')) {
console.log('โ Invalid email format');
return;
}
// ๐ก๏ธ Check for duplicate email
const existingUser = this.items.find(u => u.email === user.email);
if (existingUser) {
console.log('โ Email already exists');
return;
}
const newUser: User = { ...user, id: this.nextId++ };
this.items.push(newUser);
console.log(`โ
Added user: ${newUser.name} (ID: ${newUser.id})`);
}
// ๐ Override findById
findById(id: number): User | undefined {
return this.items.find(user => user.id === id);
}
// ๐ Override update with validation
update(id: number, updates: Partial<User>): boolean {
const userIndex = this.items.findIndex(user => user.id === id);
if (userIndex === -1) {
console.log('โ User not found');
return false;
}
// ๐ก๏ธ Validate email if being updated
if (updates.email && !updates.email.includes('@')) {
console.log('โ Invalid email format');
return false;
}
this.items[userIndex] = { ...this.items[userIndex], ...updates };
console.log(`โ
Updated user ID ${id}`);
return true;
}
// ๐ Override delete
delete(id: number): boolean {
const userIndex = this.items.findIndex(user => user.id === id);
if (userIndex === -1) {
console.log('โ User not found');
return false;
}
const deletedUser = this.items.splice(userIndex, 1)[0];
console.log(`๐๏ธ Deleted user: ${deletedUser.name}`);
return true;
}
// ๐ User-specific methods
findByEmail(email: string): User | undefined {
return this.items.find(user => user.email === email);
}
}
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Changing Method Signatures
class Parent {
greet(name: string): string {
return `Hello, ${name}!`;
}
}
// โ Wrong - changing the signature!
class BadChild extends Parent {
greet(name: string, age: number): string { // ๐ฅ Different signature!
return `Hello, ${name}, age ${age}!`;
}
}
// โ
Correct - keep the same signature
class GoodChild extends Parent {
greet(name: string): string { // โ
Same signature
return `Hey there, ${name}! Welcome!`;
}
}
๐คฏ Pitfall 2: Forgetting Return Types
class Parent {
calculate(): number {
return 42;
}
}
// โ Wrong - returning different type!
class BadChild extends Parent {
calculate(): string { // ๐ฅ TypeScript error!
return "42";
}
}
// โ
Correct - same return type
class GoodChild extends Parent {
calculate(): number { // โ
Same return type
return 84; // Different value, same type
}
}
๐ Pitfall 3: Breaking the Liskov Substitution Principle
class Bird {
fly(): void {
console.log('Flying high! ๐ฆ
');
}
}
// โ Problematic - penguins can't fly!
class Penguin extends Bird {
fly(): void {
throw new Error('Penguins cannot fly!'); // ๐ฅ Breaks expectations
}
}
// โ
Better design - composition over inheritance
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
class Eagle implements Flyable {
fly(): void {
console.log('Soaring through the sky! ๐ฆ
');
}
}
class Penguin implements Swimmable {
swim(): void {
console.log('Swimming gracefully! ๐ง');
}
}
๐ ๏ธ Best Practices
- ๐ฏ Keep Same Signatures: Donโt change parameter types or return types
- ๐ Follow LSP: Overridden methods should be substitutable
- ๐ Use super() Wisely: Enhance rather than completely replace when possible
- ๐ก๏ธ Validate Consistently: Maintain or strengthen preconditions
- ๐ Document Changes: Make overriding behavior clear to users
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Payment Processing System
Create a payment system where different payment methods override processing behavior:
๐ Requirements:
- โ
Base
PaymentProcessor
class with amount, currency, fees - ๐ณ
CreditCardProcessor
with validation and fraud detection - ๐ฆ
BankTransferProcessor
with clearing times and limits - ๐ฐ
CryptoProcessor
with blockchain confirmation and volatility - ๐ Each processor should override processing methods uniquely
๐ Bonus Points:
- Add refund processing with different rules
- Implement transaction logging
- Create payment analytics
๐ก Solution
๐ Click to see solution
// ๐ณ Base payment processor
abstract class PaymentProcessor {
amount: number;
currency: string;
baseFeeRate: number = 0.025; // 2.5%
status: 'pending' | 'processing' | 'completed' | 'failed' = 'pending';
transactionId: string;
constructor(amount: number, currency: string = 'USD') {
this.amount = amount;
this.currency = currency;
this.transactionId = `txn_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`;
}
// ๐ฐ Calculate fees - can be overridden
calculateFees(): number {
return this.amount * this.baseFeeRate;
}
// ๐ Validate transaction - basic validation
validateTransaction(): boolean {
if (this.amount <= 0) {
console.log('โ Invalid amount');
return false;
}
if (!this.currency) {
console.log('โ Currency required');
return false;
}
return true;
}
// ๐ Get transaction summary
getTransactionSummary(): string {
const fees = this.calculateFees();
const total = this.amount + fees;
return `๐ณ ${this.transactionId} | ${this.currency} ${this.amount.toFixed(2)} + ${fees.toFixed(2)} fees = ${total.toFixed(2)} total | Status: ${this.status.toUpperCase()}`;
}
// ๐ฏ Abstract method - must be overridden
abstract processPayment(): Promise<boolean>;
}
// ๐ณ Credit card processor
class CreditCardProcessor extends PaymentProcessor {
cardNumber: string;
expiryDate: string;
cvv: string;
fraudScore: number = 0;
constructor(amount: number, cardNumber: string, expiryDate: string, cvv: string, currency: string = 'USD') {
super(amount, currency);
this.cardNumber = cardNumber;
this.expiryDate = expiryDate;
this.cvv = cvv;
this.baseFeeRate = 0.029; // 2.9% for credit cards
}
// ๐ Override fee calculation with credit card fees
calculateFees(): number {
let baseFees = super.calculateFees();
// ๐ณ Premium card types have higher fees
if (this.cardNumber.startsWith('4')) {
baseFees *= 1.1; // Visa surcharge
} else if (this.cardNumber.startsWith('5')) {
baseFees *= 1.15; // Mastercard surcharge
}
// ๐ International transaction fees
if (this.currency !== 'USD') {
baseFees += this.amount * 0.01; // 1% international fee
}
return baseFees;
}
// ๐ Override validation with card-specific checks
validateTransaction(): boolean {
// ๐ Basic validation first
if (!super.validateTransaction()) {
return false;
}
// ๐ณ Credit card validation
if (this.cardNumber.length < 13 || this.cardNumber.length > 19) {
console.log('โ Invalid card number length');
return false;
}
if (this.cvv.length < 3 || this.cvv.length > 4) {
console.log('โ Invalid CVV');
return false;
}
// ๐
Expiry date check (simplified)
const today = new Date();
const [month, year] = this.expiryDate.split('/');
const expiryDate = new Date(2000 + parseInt(year), parseInt(month) - 1);
if (expiryDate < today) {
console.log('โ Card expired');
return false;
}
return true;
}
// ๐ก๏ธ Fraud detection
private performFraudCheck(): boolean {
// ๐ Simplified fraud scoring
this.fraudScore = 0;
// Large amount risk
if (this.amount > 1000) this.fraudScore += 20;
if (this.amount > 5000) this.fraudScore += 30;
// International transaction risk
if (this.currency !== 'USD') this.fraudScore += 15;
// Velocity check (simplified - in reality would check recent transactions)
if (Math.random() < 0.1) this.fraudScore += 25; // Random velocity hit
console.log(`๐ Fraud score: ${this.fraudScore}/100`);
if (this.fraudScore > 50) {
console.log('๐จ High fraud risk - transaction blocked');
return false;
}
return true;
}
// ๐ Override payment processing
async processPayment(): Promise<boolean> {
console.log(`๐ณ Processing credit card payment: ${this.getTransactionSummary()}`);
this.status = 'processing';
// ๐ก๏ธ Validation
if (!this.validateTransaction()) {
this.status = 'failed';
return false;
}
// ๐ Fraud check
if (!this.performFraudCheck()) {
this.status = 'failed';
return false;
}
// ๐ณ Simulate card processing
console.log('๐ณ Contacting card issuer...');
await new Promise(resolve => setTimeout(resolve, 1000));
// ๐ฒ Simulate approval (90% success rate)
if (Math.random() < 0.9) {
this.status = 'completed';
console.log('โ
Credit card payment approved!');
console.log(`๐ณ Charged ${this.cardNumber.slice(-4)} for ${this.currency} ${(this.amount + this.calculateFees()).toFixed(2)}`);
return true;
} else {
this.status = 'failed';
console.log('โ Credit card payment declined');
return false;
}
}
// ๐ณ Card-specific method
getMaskedCardNumber(): string {
return `****-****-****-${this.cardNumber.slice(-4)}`;
}
}
// ๐ฆ Bank transfer processor
class BankTransferProcessor extends PaymentProcessor {
bankAccountNumber: string;
routingNumber: string;
accountType: 'checking' | 'savings';
dailyLimit: number = 10000;
processingTime: number = 1; // 1 day
constructor(amount: number, bankAccountNumber: string, routingNumber: string, accountType: 'checking' | 'savings', currency: string = 'USD') {
super(amount, currency);
this.bankAccountNumber = bankAccountNumber;
this.routingNumber = routingNumber;
this.accountType = accountType;
this.baseFeeRate = 0.01; // 1% for bank transfers
}
// ๐ Override fee calculation - lower fees but flat rate for large amounts
calculateFees(): number {
const percentageFee = super.calculateFees();
const flatFee = 5.00; // $5 flat fee
// ๐ฆ Use flat fee for large amounts if it's cheaper
if (this.amount > 2000) {
return Math.min(percentageFee, flatFee);
}
return Math.max(percentageFee, flatFee); // Minimum $5 fee
}
// ๐ Override validation with bank-specific checks
validateTransaction(): boolean {
if (!super.validateTransaction()) {
return false;
}
// ๐ฆ Bank account validation
if (this.bankAccountNumber.length < 8 || this.bankAccountNumber.length > 12) {
console.log('โ Invalid account number length');
return false;
}
if (this.routingNumber.length !== 9) {
console.log('โ Invalid routing number');
return false;
}
// ๐ฐ Daily limit check
if (this.amount > this.dailyLimit) {
console.log(`โ Amount exceeds daily limit of $${this.dailyLimit.toLocaleString()}`);
return false;
}
return true;
}
// ๐ Override payment processing with bank clearing
async processPayment(): Promise<boolean> {
console.log(`๐ฆ Processing bank transfer: ${this.getTransactionSummary()}`);
this.status = 'processing';
if (!this.validateTransaction()) {
this.status = 'failed';
return false;
}
// ๐ฆ Simulate bank verification
console.log('๐ฆ Verifying bank account...');
await new Promise(resolve => setTimeout(resolve, 800));
console.log(`โณ Bank transfer initiated - will clear in ${this.processingTime} business day(s)`);
// ๐ฒ Very high success rate for bank transfers
if (Math.random() < 0.95) {
this.status = 'completed';
console.log('โ
Bank transfer authorized!');
console.log(`๐ฆ Debiting account ****${this.bankAccountNumber.slice(-4)} for ${this.currency} ${(this.amount + this.calculateFees()).toFixed(2)}`);
return true;
} else {
this.status = 'failed';
console.log('โ Bank transfer failed - insufficient funds');
return false;
}
}
// ๐ฆ Bank-specific method
getAccountSummary(): string {
return `${this.accountType.toUpperCase()} ****${this.bankAccountNumber.slice(-4)} (Routing: ${this.routingNumber})`;
}
}
// โฟ Cryptocurrency processor
class CryptoProcessor extends PaymentProcessor {
walletAddress: string;
cryptoCurrency: 'BTC' | 'ETH' | 'USDC';
networkFees: number = 0;
confirmationsRequired: number = 3;
volatilityRisk: number = 0.05; // 5% volatility buffer
constructor(amount: number, walletAddress: string, cryptoCurrency: 'BTC' | 'ETH' | 'USDC', currency: string = 'USD') {
super(amount, currency);
this.walletAddress = walletAddress;
this.cryptoCurrency = cryptoCurrency;
this.baseFeeRate = 0.015; // 1.5% for crypto
// ๐ Set network fees based on crypto type
this.networkFees = cryptoCurrency === 'BTC' ? 15 : cryptoCurrency === 'ETH' ? 25 : 2;
}
// ๐ Override fee calculation with network fees and volatility
calculateFees(): number {
const baseFees = super.calculateFees();
const volatilityBuffer = this.amount * this.volatilityRisk;
console.log(`โฟ Base fees: $${baseFees.toFixed(2)}, Network fees: $${this.networkFees}, Volatility buffer: $${volatilityBuffer.toFixed(2)}`);
return baseFees + this.networkFees + volatilityBuffer;
}
// ๐ Override validation with crypto-specific checks
validateTransaction(): boolean {
if (!super.validateTransaction()) {
return false;
}
// โฟ Wallet address validation (simplified)
if (this.walletAddress.length < 26 || this.walletAddress.length > 62) {
console.log('โ Invalid wallet address format');
return false;
}
// ๐ฐ Minimum amount for crypto (due to network fees)
if (this.amount < 10) {
console.log('โ Minimum transaction amount is $10 for crypto payments');
return false;
}
return true;
}
// โฟ Get current crypto rate (simulated)
private getCryptoRate(): number {
const basRates = { BTC: 45000, ETH: 3000, USDC: 1 };
const baseRate = baseRates[this.cryptoCurrency];
// ๐ Add some volatility
const volatility = (Math.random() - 0.5) * 0.1; // ยฑ5% volatility
return baseRate * (1 + volatility);
}
// ๐ Override payment processing with blockchain
async processPayment(): Promise<boolean> {
console.log(`โฟ Processing crypto payment: ${this.getTransactionSummary()}`);
this.status = 'processing';
if (!this.validateTransaction()) {
this.status = 'failed';
return false;
}
// โฟ Get current rates
const cryptoRate = this.getCryptoRate();
const cryptoAmount = (this.amount + this.calculateFees()) / cryptoRate;
console.log(`โฟ Converting ${this.currency} ${(this.amount + this.calculateFees()).toFixed(2)} to ${cryptoAmount.toFixed(8)} ${this.cryptoCurrency}`);
console.log(`โฟ Current rate: 1 ${this.cryptoCurrency} = $${cryptoRate.toFixed(2)}`);
// ๐ Simulate blockchain transaction
console.log('๐ Broadcasting transaction to blockchain...');
await new Promise(resolve => setTimeout(resolve, 1200));
console.log(`โณ Waiting for ${this.confirmationsRequired} confirmations...`);
// ๐ฒ High success rate but some network issues possible
if (Math.random() < 0.88) {
this.status = 'completed';
console.log('โ
Crypto payment confirmed on blockchain!');
console.log(`โฟ Sent ${cryptoAmount.toFixed(8)} ${this.cryptoCurrency} to ${this.walletAddress.slice(0, 8)}...`);
return true;
} else {
this.status = 'failed';
console.log('โ Crypto payment failed - network congestion');
return false;
}
}
// โฟ Crypto-specific method
getWalletSummary(): string {
return `${this.cryptoCurrency} wallet: ${this.walletAddress.slice(0, 8)}...${this.walletAddress.slice(-8)}`;
}
}
// ๐ณ Payment processor factory
class PaymentProcessorFactory {
static createProcessor(type: 'credit_card' | 'bank_transfer' | 'crypto', amount: number, ...args: any[]): PaymentProcessor {
switch (type) {
case 'credit_card':
return new CreditCardProcessor(amount, ...args);
case 'bank_transfer':
return new BankTransferProcessor(amount, ...args);
case 'crypto':
return new CryptoProcessor(amount, ...args);
default:
throw new Error('Unknown payment processor type');
}
}
}
// ๐ฎ Let's process some payments!
async function runPaymentDemo() {
console.log('๐ณ PAYMENT PROCESSING DEMO ๐ณ\n');
// Create different payment processors
const creditCard = new CreditCardProcessor(299.99, '4111111111111111', '12/25', '123');
const bankTransfer = new BankTransferProcessor(1500.00, '123456789012', '021000021', 'checking');
const crypto = new CryptoProcessor(750.00, 'bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh', 'BTC');
console.log('=== Payment Summaries ===');
console.log(creditCard.getTransactionSummary());
console.log(bankTransfer.getTransactionSummary());
console.log(crypto.getTransactionSummary());
console.log('\n=== Processing Credit Card Payment ===');
await creditCard.processPayment();
console.log('\n=== Processing Bank Transfer ===');
await bankTransfer.processPayment();
console.log('\n=== Processing Crypto Payment ===');
await crypto.processPayment();
console.log('\n=== Final Status ===');
console.log(`๐ณ Credit Card: ${creditCard.status.toUpperCase()}`);
console.log(`๐ฆ Bank Transfer: ${bankTransfer.status.toUpperCase()}`);
console.log(`โฟ Crypto: ${crypto.status.toUpperCase()}`);
}
// Run the demo
runPaymentDemo();
๐ Key Takeaways
Youโve mastered method overriding! Hereโs what you can now do:
- โ Override methods effectively while keeping signatures ๐
- โ Combine overriding with super calls for enhancement ๐
- โ Create polymorphic behavior with consistent interfaces ๐ญ
- โ Validate and handle edge cases in overridden methods ๐ก๏ธ
- โ Avoid common overriding pitfalls like a pro ๐ฏ
Remember: Method overriding is about customization, not replacement - enhance what works! ๐
๐ค Next Steps
Congratulations! ๐ Youโve mastered method overriding in TypeScript!
Hereโs what to do next:
- ๐ป Practice with the payment processing exercise above
- ๐๏ธ Build your own system with different overriding patterns
- ๐ Move on to our next tutorial: Abstract Classes and Methods: Creating Base Classes
- ๐ Experiment with combining overriding, generics, and interfaces!
Remember: Every great object-oriented system uses method overriding effectively. Keep customizing, keep learning, and override your way to better code! ๐
Happy coding! ๐๐โจ