Skip to main content

👨‍👩‍👧 Inheritance

Children inheriting traits from parents

The Family Tree Analogy

Think about your family:

  • You inherited traits from your parents - maybe your dad's eye color, your mom's height
  • But you also have unique traits they don't have
  • And you might do some things differently than they do

Inheritance in programming works exactly the same way.

A child class inherits everything from a parent class:

  • All the parent's properties (data)
  • All the parent's methods (behavior)

Then it can:

  • Add its own unique properties and methods
  • Change (override) inherited behaviors

Why Do We Need Inheritance?

The Problem: Repeated Code

Imagine coding a zoo simulation without inheritance:

Dog: name, age, eat(), sleep(), bark()
Cat: name, age, eat(), sleep(), meow()
Bird: name, age, eat(), sleep(), fly()
Fish: name, age, eat(), sleep(), swim()

See the repetition? name, age, eat(), sleep() are the same for ALL animals!

The Solution: Inheritance

Create one "Animal" class with the common stuff, then specialize:

Animal: name, age, eat(), sleep()
    ↓ inherits ↓
  Dog: bark()    (+ everything from Animal)
  Cat: meow()    (+ everything from Animal)
  Bird: fly()    (+ everything from Animal)
  Fish: swim()   (+ everything from Animal)

Now if you need to change how eat() works, you change it in ONE place!


The Parent-Child Relationship

Think of it as a family tree:

          [Parent Class]
               Animal
              /      \
             /        \
    [Child Classes]
       Dog            Cat

The parent is also called:

  • Base class (the foundation)
  • Super class (the one above)

The child is also called:

  • Derived class (derived from parent)
  • Sub class (below the parent)

Key rule: The child "IS A" type of parent.

  • Dog IS A Animal âś“
  • Cat IS A Animal âś“
  • Car is not an Animal âś—

How It Works in Code

Step 1: Define the parent class

class Animal {
  constructor(name) {
    this.name = name;
  }

  eat() {
    console.log(this.name + " is eating");
  }

  sleep() {
    console.log(this.name + " is sleeping");
  }
}

Step 2: Create child class using extends

class Dog extends Animal {
  // Call parent constructor with super()
  constructor(name, breed) {
    super(name); // This runs Animal's constructor
    this.breed = breed; // Add new property
  }

  // Add new method (dogs can bark)
  bark() {
    console.log(this.name + " says woof!");
  }
}

Step 3: The child has EVERYTHING

const max = new Dog("Max", "Labrador");

// Inherited from Animal:
max.eat(); // "Max is eating"
max.sleep(); // "Max is sleeping"

// Its own:
max.bark(); // "Max says woof!"

The super Keyword

super lets the child talk to its parent:

In constructor: Call parent's constructor first

constructor(name, breed) {
  super(name);  // Typically comes first
  this.breed = breed;
}

In methods: Call parent's version of a method

speak() {
  super.speak();  // Run parent's speak() first
  console.log("And also barks!");
}

Overriding: Changing Inherited Behavior

Children can replace inherited methods:

Animal.speak() → "Makes a sound"

Dog overrides with:
Dog.speak() → "Barks"

Cat overrides with:
Cat.speak() → "Meows"

The child's version runs instead of the parent's.

Example: All employees work, but different types work differently:

  • Generic Employee: "Working"
  • Developer: "Writing code"
  • Manager: "In meetings"

Real-World Examples

1. Website Users

User: username, email, login(), logout()
    ↓
  Admin: deleteUser(), banUser()  (+ User stuff)
  Moderator: editPosts()          (+ User stuff)
  Member: createPost()            (+ User stuff)

2. Game Characters

Character: health, position, move(), takeDamage()
    ↓
  Warrior: sword, slash()        (+ Character stuff)
  Mage: mana, castSpell()        (+ Character stuff)
  Archer: arrows, shoot()        (+ Character stuff)

3. Shapes

Shape: color, getArea()
    ↓
  Circle: radius, getArea() → πr²
  Rectangle: width, height, getArea() → w×h
  Triangle: base, height, getArea() → ½bh

Inheritance vs Composition

Not everything should use inheritance. Ask yourself:

"IS-A" → Use Inheritance

  • Dog IS A Animal âś“
  • Car IS A Vehicle âś“

"HAS-A" → Use Composition

  • Car HAS A Engine (not: Car IS A Engine)
  • House HAS A Kitchen (not: House IS A Kitchen)
❌ class Car extends Engine  // Car is not an engine!
âś“ class Car { engine = new Engine() }  // Car has an engine

Rule of thumb: When in doubt, prefer composition. It's more flexible.


Common Mistakes

Mistake 1: Deep Inheritance Chains

❌ Bad:
Animal → Mammal → Canine → Dog → Labrador → YellowLabrador

âś“ Good:
Animal → Dog  (keep it shallow!)

Deep chains are confusing and fragile. Try to keep inheritance to 2-3 levels max.

Mistake 2: Forgetting super() in Constructor

class Child extends Parent {
  constructor(name) {
    // ❌ Forgot super() - ERROR!
    this.name = name;
  }
}

class Child extends Parent {
  constructor(name) {
    super(); // âś“ Typically first
    this.name = name;
  }
}

Mistake 3: Inheriting Mainly for Code Reuse

If there's no real "is-a" relationship, don't use inheritance just to share code. Use utilities or composition instead.

❌ Rectangle extends Logger  // Makes no sense!
âś“ Rectangle uses Logger     // Better

FAQ

Q: Can a class have multiple parents?

In JavaScript, generally no - it's single inheritance. One class can extend one parent. Some languages (Python, C++) allow multiple inheritance, but it gets complicated.

Q: What's the difference between extends and implements?

extends = inheritance (getting code from parent) implements = interface (promising to have certain methods)

TypeScript has both. JavaScript typically uses extends for inheritance.

Q: When should I use inheritance?

When there's a TRUE "is-a" relationship with shared behavior. Ask: "Would it make sense to say 'X is a Y'?"

Q: Can I prevent a method from being overridden?

Not in vanilla JavaScript. In TypeScript, you can use final or readonly patterns.

Q: What if I don't want the parent constructor to run?

You generally need to call super() in a child constructor. If you don't want certain parent setup, you might need to rethink your class design.

Q: Is inheritance "bad"?

No, but it's often overused. Composition is usually more flexible. Use inheritance when there's a clear hierarchical "is-a" relationship.


Summary

Inheritance lets child classes receive properties and methods from parent classes, creating hierarchies where specialized classes build upon general ones.

Key Takeaways:

  • Child inherits everything from parent
  • Use extends to create inheritance
  • super() calls parent constructor (required!)
  • super.method() calls parent's version
  • Children can override parent methods
  • Use when there's a clear "IS-A" relationship
  • Keep hierarchies shallow (2-3 levels max)
  • When in doubt, prefer composition

Inheritance is a powerful tool, but like any tool, use it where it fits. Good code often mixes inheritance with composition.

Leave a Comment

Comments (0)

Be the first to comment on this concept.

Comments are approved automatically.