CHALLENGE 3: Inheritance and Overriding ======================================== SOLUTION: class Animal { name: string; constructor(name: string) { this.name = name; } speak(): void { console.log(`${this.name} makes a sound`); } } class Dog extends Animal { speak(): void { console.log(`${this.name} says Woof!`); } } class Cat extends Animal { speak(): void { console.log(`${this.name} says Meow!`); } } // Using the classes const genericAnimal = new Animal("Generic"); const myDog = new Dog("Buddy"); const myCat = new Cat("Whiskers"); genericAnimal.speak(); // Output: Generic makes a sound myDog.speak(); // Output: Buddy says Woof! myCat.speak(); // Output: Whiskers says Meow! EXPLANATION: - "class Dog extends Animal" — Dog inherits from Animal - "constructor(name: string)" — Dog needs to initialize itself - (In this case, we inherit Animal's constructor implicitly) - "speak(): void" — overrides the parent's speak() method - Each subclass provides its own implementation MORE REALISTIC: Using super() class Dog extends Animal { breed: string; constructor(name: string, breed: string) { super(name); // Call parent constructor this.breed = breed; } speak(): void { // Optionally call parent method first super.speak(); // Output: Buddy makes a sound console.log(`${this.name} says Woof!`); // Output: Buddy says Woof! } } const myDog = new Dog("Buddy", "Golden Retriever"); myDog.speak(); // Output: // Buddy makes a sound // Buddy says Woof! POLYMORPHISM IN ACTION: function makeAnimalSpeak(animal: Animal): void { animal.speak(); } makeAnimalSpeak(new Animal("Generic")); // Generic makes a sound makeAnimalSpeak(new Dog("Rex")); // Rex says Woof! makeAnimalSpeak(new Cat("Mittens")); // Mittens says Meow! WHY THIS MATTERS: - Different types, same interface (all have speak()) - You can treat Dog and Cat as Animal in a function - Each type behaves according to its own speak() implementation - This is the power of polymorphism: write once, work with many types KEY DIFFERENCES: - Animal.speak(): generic sound - Dog.speak(): "Woof!" (overridden) - Cat.speak(): "Meow!" (overridden) METHOD OVERRIDING RULES: ✅ Subclass method has same signature (name, parameters, return type) ✅ Subclass can make the implementation more specific ✅ Subclass can call parent method with super.speak() ❌ Can't change the parameter types or return type (breaks the contract)