The sixty-four-dollar question: what’s the difference between implementing a and extending a class? Let’s make things clear in no time.
In This Article:
Extending a class
Let’s say we have a class that represents a random movie. Every movie needs to be played or paused. So our class will look like this:
class Movie {
play() {
console.log(`We are watching a random Movie`);
}
pause() {
console.log(`Pausing the movie..`);
}
}
Now suppose we want to write a class that represents a particular genre of movie, let’s say a western movie.
We should rewrite our play()
and pause()
methods but we know that (almost) always repeating code is bad. So?
So we will write a class that extends Movie and inherits all its methods.
We can add new methods and properties specific to this class, such as the number of shots fired in the movie. Or we can override existing methods. Try this code in your browser console:
class Movie {
constructor(data) {
this.title = data.title;
this.director = data.director;
}
play() {
console.log(`We are watching a random Movie`);
}
pause() {
console.log(`Pausing the movie..`);
}
}
class Western extends Movie {
constructor(data) {
super(data)
this.shotsFired = 110;
}
play() {
console.log(`We are watching ${this.title} by ${this.director}!`);
console.log(`${this.shotsFired} shots fired so far`);
}
}
const rioBravo = new Western({
title: 'Rio Bravo',
director: 'John Ford'
});
rioBravo.play(); // We are watching a Rio Bravo by John Ford! 110 shots fired so far
rioBravo.pause(); // Pausing the movie..
This way we can have infinite genres: comedy, romance, noir… without ever repeating our code.
Implementing a class
The goal here is not to write less code, but to write better code.
An interface in fact only serves to establish a contract with the developer who will be forced to implement his class in that way. This will avoid many errors and side effects.
As you can see in the code below, the Movie
interface will force the developer to write a class that contains a title
and a director
as strings,
play()
and pause()
methods, and an optional year
(it’s optional because we have this syntax with the question mark year?:
).
If implemented, year
must be a number.
interface Movie {
title: string;
director: string;
year?: number;
play(): void;
pause(): void;
}
class Western implements Movie {
title: string;
director: string;
constructor(data: MovieData) {
this.title = data.title;
this.director = data.director;
}
play(): void {
console.log(`We are watching ${this.title} by ${this.director}!`);
}
pause(): void {
console.log(`Pausing the movie..`);
}
}
const rioBravo = new Western({
title: 'Rio Bravo',
director: 'John Ford',
year: 1950
});
If even one rule of the contract is broken, the compiler will return an error.
Of course also MovieData
will be an interface
Wrapping up
To recap, the key differences between the two keywords are.
Extends | Implements |
---|---|
Both JavaScript and TypeScript | Only TypeScript |
The new class is a child | The new class is the implementation of the interface |
The new class inherits all properties and methods of the parent and can add new ones. | The class must implement its own methods |
The new class has no constraints | The class is forced to adhere to the contract established with the interface |
Each class can be extended and still can be used as a normal class | The interface is not concrete class, it can’t be used as is |
Useful for code reusability | Useful for type safety and code maintainability |
Multiple inheritance is not supported | Multiple interface is supported |