The idea
What it is
Inheritance lets one class build on another. You write a general parent class once, then create child classes that automatically get all of its fields and methods — and add or change only what's different. Less copy-paste, one place to fix bugs.
Think of animals. Every animal has a name, can eat(), and can sleep(). A dog is an animal, so it gets all of that for free — but it also barks instead of making a generic noise, and it can fetch(). A cat is an animal too: same shared behavior, but it meows and can scratch(). You describe the shared parts once in Animal, then let each species specialize.
The one sentence to remember
A subclass inherits what the parent already does, adds new behavior of its own, and overrides the few methods it wants to do differently. Reuse plus specialization, in one relationship.
Mechanics
How it works
Base class vs. derived class
The base class (also called the parent or superclass) holds the shared, general behavior — here, Animal. A derived class (the child or subclass) extends it — Dog, Cat, Bird. The derived class starts with everything the base has, then layers its own changes on top.
In most languages you spell this with the extends keyword. class Dog extends Animal reads almost like English: a Dog extends what an Animal already is.
Inheriting fields and methods
Because Dog extends Animal, every Dog automatically has a name field and can eat() and sleep() — you didn't rewrite any of that. Call myDog.eat() and it runs the exact code defined up in Animal. That's inheritance: the child reuses the parent's members as if they were its own.
class Animal {
constructor(public name: string) {}
eat() {
return this.name + " is eating";
}
makeSound() {
return "(some generic animal noise)";
}
}
class Dog extends Animal {
constructor(name: string) {
super(name); // run Animal's constructor first
}
// OWN method — only dogs have this
fetch() {
return this.name + " fetches the ball";
}
// OVERRIDE — replaces Animal's makeSound for dogs
makeSound() {
return "Woof!";
}
}
const d = new Dog("Rex");
d.eat(); // inherited from Animal → "Rex is eating"
d.makeSound(); // overridden by Dog → "Woof!"
d.fetch(); // Dog's own method → "Rex fetches the ball"Calling the parent with `super`
When a subclass has its own constructor, it must first set up the part of the object that the parent owns. super(name) calls the parent's constructor so the inherited name field gets initialized before the child adds anything. You can also use super.someMethod() inside an override to reuse the parent's version and add to it, rather than replacing it entirely.
Overriding vs. inheriting
If a subclass defines a method with the same name as one in the parent, the subclass's version overrides it. After that, calling the method on a Dog runs the Dog version — "Woof!" — not the generic Animal one. Methods the subclass doesn't redefine are simply inherited and run the parent's code unchanged. So a Dog inherits eat() and sleep() but overrides makeSound().
The "is-a" test
Inheritance models an is-a relationship. Before writing B extends A, say it out loud: "a Dog is an Animal" — true, so inheritance fits. "a Car is an Engine"? No — a car has an engine. That's a different relationship, and inheritance would be the wrong tool there.
Single vs. multiple inheritance
Some languages let a class inherit from only one parent — Java and C# are single-inheritance (you then mix in extra capabilities through interfaces). Others, like C++ and Python, allow multiple inheritance, where a class can extend several parents at once. Multiple inheritance is powerful but can get confusing when two parents define the same method, so single inheritance plus interfaces is the more common default.
Prefer composition when it isn't truly "is-a"
Inheritance is tempting as a shortcut for code reuse, but reach for it only when the is-a test genuinely holds. If a type merely needs some behavior — but isn't really a kind of the parent — prefer composition: hold the other object as a field and delegate to it. We'll dig into composition over inheritance in a later lesson; for now, just remember that not every "I want to reuse this" is an inheritance.
Interactive prototype
See it. Build it. Break it.
A sandboxed, hands-on simulation — no setup, no install. Play with it as you read.
About this simulation
At the top sits the base class Animal with its members. Below it, Dog, Cat, and Bird branch off — each one inherits everything from Animal, adds its own method, and overrides makeSound(). Click a subclass, then call its methods and watch the console say exactly where each method came from.
Hands-on
Try these yourself
Open the prototype above, predict what happens, then verify.
Pick a subclass and read its members
Click Dog, then Cat, then Bird in the family tree. Watch the instance card relabel every member: things tagged inherited came straight from Animal, own is the method that subclass added (like fetch()), and overridden is makeSound(), which each species redefines.
Call an inherited method
With any subclass selected, press eat() or sleep(). The console narrates → inherited from Animal and the highlight in the tree jumps up to Animal, proving the code that runs lives in the parent — the subclass didn't rewrite it.
See an override resolve
Press makeSound() and notice the console says Dog overrides → "Woof!" — the highlight stays on the subclass, not Animal. Then press the subclass-only button (fetch() for Dog, scratch() for Cat, fly() for Bird); it's available only while that subclass is selected, because it's its own method.
In practice
When to use it — and what trips people up
When inheritance is the right tool
- When the relationship is a genuine is-a — a
Dogis anAnimal, aSavingsAccountis aBankAccount. Say it aloud; if it sounds wrong, it probably is. - When several types share real, common behavior that you want to define once in a parent and reuse everywhere.
- When you want polymorphism — treating different subclasses uniformly through the parent type (loop over a list of
Animaland callmakeSound()on each).
When to prefer composition instead
If you're inheriting just to grab some handy code — but the child isn't truly a kind of the parent — stop. Use composition: hold the helper as a field and call it. Composition is more flexible, avoids fragile hierarchies, and doesn't lie about the relationship. Inheritance is for is-a; composition is for has-a.
What it gives you
- Reuse — write shared fields and methods once in the parent, and every subclass gets them for free.
- Enables polymorphism — code can work against the base type and let each subclass supply its own behavior.
- Models real hierarchies cleanly when an honest is-a relationship exists.
- Centralizes change — fix or extend the parent and every subclass benefits at once.
Common mistakes
- Deep, sprawling hierarchies become fragile — a small change in a base class can ripple into every descendant.
- Inheriting purely for code reuse (when it isn't really is-a) couples unrelated types and invites composition's job onto the wrong tool.
- A careless override can break the parent's contract (the Liskov Substitution Principle), so subclasses no longer behave as the base promised.
- Subclasses are tightly coupled to their parent's internals, making the base class hard to change without breaking children.
Reference
Code & further reading
A minimal reference implementation and pointers worth bookmarking.
class Animal {
constructor(public name: string) {}
eat() {
return this.name + " is eating";
}
makeSound() {
return "(generic animal noise)";
}
}
class Dog extends Animal {
constructor(name: string) {
super(name); // initialize the inherited 'name' field
}
// own method — added by Dog
fetch() {
return this.name + " fetches the ball";
}
// override — Dog's own version of makeSound
makeSound() {
return "Woof!";
}
}
const rex = new Dog("Rex");
rex.eat(); // inherited → "Rex is eating"
rex.makeSound(); // overridden → "Woof!"
rex.fetch(); // own → "Rex fetches the ball"References & further reading
5 sources- Docsdocs.oracle.com
Oracle — Inheritance (Java Tutorial)
The canonical walkthrough of subclasses, extends, and super from the official Java tutorial.
- Docsdeveloper.mozilla.org
MDN — extends
Reference for the
extendskeyword in JavaScript, with super and override examples. - Docsdocs.python.org
Python docs — Inheritance
How inheritance, super().__init__, and multiple inheritance work in Python.
- Articleen.wikipedia.org
Wikipedia — Inheritance (object-oriented programming)
A broad, language-agnostic survey of inheritance and where it fits in OOP.
- Articleen.wikipedia.org
Wikipedia — Composition over inheritance
The classic counterpoint — when to favor has-a composition over is-a inheritance.
Knowledge check
Did it land?
Quick questions, answers revealed on submit. Nothing is scored or saved.
question 01 / 04
What does it mean for Dog to extend Animal?
question 02 / 04
Inside Dog's constructor you write super(name). What is it for?
question 03 / 04
Dog defines its own makeSound() returning "Woof!", but does not redefine eat(). What runs for each call on a Dog?
question 04 / 04
You want a Car to reuse some logic from an Engine class. Should Car extends Engine?
0/4 answered