How to extend an interface to create a combination of interfaces?


In TypeScript, interfaces provide a powerful way to define the shape of objects and enforce type constraints. They allow us to specify the required properties and methods that an object must have. One interesting feature of interfaces is the ability to extend them, allowing us to create a combination of interfaces. In this tutorial, we will explore how to extend interfaces to create new ones that inherit properties and methods from existing interfaces.

Syntax

interface NewInterface extends ExistingInterface {
   // Additional properties and methods
}

interface NewInterface extends Interface1, Interface2, ... {
   // Additional properties and methods
}

The syntax to create a new interface by extending an existing one is shown above. The syntax for extending multiple interfaces is also shown. The second syntax is to extend multiple interfaces.

Example 1: Extending a Single Interface

Let's start with a simple scenario where we have an interface called Shape that defines a common property color for various shapes. We want to create a new interface ColoredShape, that extends Shape and adds an additional property name.

Let's see an example of extending the Shape interface to create the ColoredShape interface. In this example, we defined the Shape interface with a color property. Then, we created the ColoredShape interface by extending Shape and adding a name property. We instantiated an object square of type ColoredShape and assigned values to color and name properties. Finally, we accessed and printed the values of color and name using dot notation.

interface Shape {
   color: string;
}

interface ColoredShape extends Shape {
   name: string;
}

const square: ColoredShape = {
   color: "red",
   name: "Square",
};

console.log(square.color);
console.log(square.name); 

On compiling, it will generate the following JavaScript code −

var square = {
   color: "red",
   name: "Square"
};
console.log(square.color);
console.log(square.name);

Output

The above code will produce the following output −

red
Square

Example 2: Extending Multiple Interfaces

In TypeScript, we can also extend multiple interfaces to create a new interface that combines properties and methods from all the extended interfaces. This allows us to create more complex and specialized interfaces by reusing existing ones.

In the example below, we defined the Printable and Scanable interfaces, each with their respective methods. Then, we created the MultifunctionalDevice interface by extending both Printable and Scanable interfaces and adding a new method copy(). We implemented the MultifunctionalDevice interface in a class called Printer and provided the necessary implementations for all the methods. Finally, we instantiated an object of the Printer class and called the print(), scan(), and copy() methods.

interface Printable {
   print(): void;
}

interface Scanable {
   scan(): void;
}

interface MultifunctionalDevice extends Printable, Scanable {
   copy(): void;
}

class Printer implements MultifunctionalDevice {
   print() {
      console.log("Printing...");
   }

   scan() {
      console.log("Scanning...");
   }

  copy() {
     console.log("Copying...");
   }
}

const printer = new Printer();
printer.print(); 
printer.scan(); 
printer.copy(); 

On compiling, it will generate the following JavaScript code −

var Printer = /** @class */ (function () {
   function Printer() {
   }
   Printer.prototype.print = function () {
      console.log("Printing...");
   };
   Printer.prototype.scan = function () {
      console.log("Scanning...");
   };
   Printer.prototype.copy = function () {
      console.log("Copying...");
   };
   return Printer;
}());
var printer = new Printer();
printer.print();
printer.scan();
printer.copy();

Output

The above code will produce the following output −

Printing...
Scanning...
Copying...

Example 3: Enhancing an Existing Interface

We may often encounter situations where we want to enhance an existing interface by adding additional properties or methods. Extending the interface allows us to do so without modifying the original interface directly. In this example, we have an existing User interface with a name property. We extend the User interface to create an EnhancedUser interface, which adds an age property and a greet() method. By extending the interface, we can define an object user of type EnhancedUser that includes the properties and methods from both interfaces.

interface User {
   name: string;
}
interface EnhancedUser extends User {
   age: number;
   greet(): void;
}
const user: EnhancedUser = {
   name: "John Wick",
   age: 25,
   greet() {
      console.log(`Hello, my name is ${this.name} and I'm ${this.age} years old.`);
   }
};
user.greet();

On compiling, it will generate the following JavaScript code −

var user = {
   name: "John Wick",
   age: 25,
   greet: function () {
      console.log("Hello, my name is ".concat(this.name, " and I'm ").concat(this.age, " years old."));
   }
};
user.greet();

Output

The above code will produce the following output −

Hello, my name is John Wick and I'm 25 years old.

Example 4: Creating Composite Interfaces

Extending interfaces can also be valuable when creating composite interfaces that combine properties and methods from multiple sources. This is particularly useful when working with external libraries or modules that provide their interfaces. In this example, we have four interfaces: Product, DiscountedProduct, ProductWithReviews, and FeaturedProduct. Each interface extends one or more existing interfaces, allowing us to create a composite interface with properties and methods from multiple sources. We then define an object product of the type FeaturedProduct, which incorporates all the properties defined in the extended interfaces.

interface Product {
   name: string;
   price: number;
}
interface DiscountedProduct extends Product {
   discount: number;
}
interface ProductWithReviews extends Product {
   reviews: string[];
}
interface FeaturedProduct extends DiscountedProduct, ProductWithReviews {
   featured: boolean;
}
const product: FeaturedProduct = {
   name: "Smartphone",
   price: 599,
   discount: 50,
   reviews: ["Great product!", "Highly recommended."],
   featured: true
};
console.log(product.featured); 
console.log(product.reviews);

On compiling, it will generate the following JavaScript code −

var product = {
   name: "Smartphone",
   price: 599,
   discount: 50,
   reviews: ["Great product!", "Highly recommended."],
   featured: true
};
console.log(product.featured);
console.log(product.reviews);

Output

The above code will produce the following output −

true
[ 'Great product!', 'Highly recommended.' ]

Example 5: Overriding Properties and Methods

We can also override properties and methods inherited from the base interfaces when extending interfaces. This allows us to modify or provide different implementations for specific properties or methods in the extended interface.

In the below example, we have an Animal interface with a name property and a makeSound() method. We extend the Animal interface to create a Dog interface. By overriding the makeSound() method in the Dog interface, we provide a different implementation specific to dogs. The object dog of type Dog can then be instantiated, and the overridden method will be invoked when called.

interface Animal {
   name: string;
   makeSound(): void;
}
interface Dog extends Animal {
   makeSound(): void;
}
const dog: Dog = {
   name: "Buddy",
   makeSound() {
      console.log("Woof woof!");
   }
};
dog.makeSound();

On compiling, it will generate the following JavaScript code −

var dog = {
   name: "Buddy",
   makeSound: function () {
      console.log("Woof woof!");
   }
};
dog.makeSound();

Output

The above code will produce the following output −

Woof woof!

Conclusion

In conclusion, extending interfaces in TypeScript provides a powerful mechanism for creating combinations of interfaces, enhancing existing interfaces, and promoting code reusability. By extending interfaces, we can inherit properties and methods from base interfaces, add additional properties and methods, override existing ones, and compose interfaces from multiple sources.

This feature allows us to build flexible and robust type systems, enabling us to define clear contracts between components and enforce type safety. It enhances our codebase's modularity, readability, and maintainability, making it easier to manage and extend our applications. Whether extending a single interface, combining multiple interfaces, enhancing existing interfaces, or overriding properties and methods, the ability to extend interfaces empowers developers to create expressive and reusable code.

Updated on: 21-Aug-2023

112 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements