Inheritance with extends
Sometimes you need several classes that share a common foundation but differ in specific ways. Inheritance lets one class (extends) build on another, gaining all of its properties and methods while adding or overriding what it needs.
The extends keyword
Section titled “The extends keyword”class RecurringExpense extends Expense { constructor({ recurrence, ...rest }) { super(rest); this.recurrence = recurrence; // 'monthly', 'weekly', etc. }}RecurringExpense is a subclass (or child class) of Expense. Every instance of RecurringExpense automatically has all the properties and methods of Expense, plus the recurrence property.
super()
Section titled “super()”The first thing a subclass constructor must do is call super(), passing the arguments the parent constructor needs. This runs the parent’s constructor and sets up this — you cannot use this before calling super.
class RecurringExpense extends Expense { constructor({ recurrence, ...rest }) { super(rest); // must come first — runs Expense's constructor this.recurrence = recurrence; }
nextDueDate() { const base = new Date(this.date); if (this.recurrence === 'monthly') { base.setMonth(base.getMonth() + 1); } else if (this.recurrence === 'weekly') { base.setDate(base.getDate() + 7); } return base.toISOString().split('T')[0]; }}
const gym = new RecurringExpense({ description: 'Gym membership', amount: 45.00, category: 'Health', date: '2024-01-10', recurrence: 'monthly',});
console.log(gym.format()); // 'Gym membership: $45.00' — inherited from Expenseconsole.log(gym.nextDueDate()); // '2024-02-10'console.log(gym instanceof Expense); // trueconsole.log(gym instanceof RecurringExpense); // truegym passes both instanceof checks — it is both an Expense and a RecurringExpense.
Overriding methods
Section titled “Overriding methods”A subclass can override a parent method by defining a method with the same name:
class RecurringExpense extends Expense { constructor({ recurrence, ...rest }) { super(rest); this.recurrence = recurrence; }
// Override Expense's format() to include recurrence info format() { return `${super.format()} (${this.recurrence})`; }}
console.log(gym.format()); // 'Gym membership: $45.00 (monthly)'super.format() calls the parent class’s version of format. This lets you extend the parent’s behavior rather than replace it entirely.
When to use inheritance
Section titled “When to use inheritance”Inheritance is powerful but easy to overuse. Use it when:
- A clear “is-a” relationship exists: a
RecurringExpenseis anExpense - The subclass genuinely needs everything the parent has
- You are specializing behavior, not just grouping unrelated functionality
Avoid inheritance when the relationship is “has-a” or when you just need to share some utility functions. In those cases, composition (having one class hold an instance of another) or a shared utility module is usually cleaner.
For BudgetBuddy, a single Expense class is enough — no inheritance needed. The RecurringExpense here is an illustrative example.
Exercise
Section titled “Exercise”- Using your
Expenseclass from the previous lessons, create aRecurringExpensesubclass that adds arecurrenceproperty ('daily','weekly','monthly'). - Override
format()inRecurringExpenseto append the recurrence, e.g.'Gym membership: $45.00 (monthly)'. - Create one
RecurringExpenseinstance and confirm:- It has the
description,amount,category, anddateproperties fromExpense - It has the
recurrenceproperty format()returns the overridden versioninstanceof Expenseistrueinstanceof RecurringExpenseistrue
- It has the
extendscreates a subclass that inherits all properties and methods from the parent.super()must be called first in a subclass constructor — it runs the parent constructor and sets upthis.super.method()calls the parent’s version of a method from inside an override.- Subclass instances pass
instanceofchecks for both the subclass and the parent. - Use inheritance for genuine “is-a” relationships; avoid it for utility sharing or “has-a” relationships.