Category: 04. Interfaces

https://cdn3d.iconscout.com/3d/premium/thumb/application-menu-3d-icon-png-download-8817448.png

  • Extending Interfaces

    In TypeScript, interfaces are 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. Extending an interface is also known as interface inheritance.

    Interface inheritance allows you to create more specialized interfaces that inherit properties and methods from other interfaces.

    Syntax

    Use extends keyword to extend a single or multiple interfaces in TypeScript.

    interfaceNewInterfaceextendsExistingInterface{// Additional properties and methods}interfaceNewInterfaceextendsInterface1, 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.

    Extending a Single Interface

    We can achieve single interface inheritance by 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.

    Example

    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.

    interfaceShape{
       color:string;}interfaceColoredShapeextendsShape{
       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

    red
    Square
    

    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. This helps to achieve multiple interface inheritance in TypeScript.

    Example

    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.

    interfacePrintable{print():void;}interfaceScanable{scan():void;}interfaceMultifunctionalDeviceextendsPrintable, Scanable {copy():void;}classPrinterimplementsMultifunctionalDevice{print(){console.log("Printing...");}scan(){console.log("Scanning...");}copy(){console.log("Copying...");}}const printer =newPrinter();
    printer.print(); 
    printer.scan(); 
    printer.copy();

    On compiling, it will generate the following JavaScript code:

    var Printer =/** @class */(function(){functionPrinter(){}
       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 =newPrinter();
    printer.print();
    printer.scan();
    printer.copy();

    Output

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

    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.

    Example

    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.

    interfaceUser{
       name:string;}interfaceEnhancedUserextendsUser{
       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

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

    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.

    Example

    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.

    interfaceProduct{
       name:string;
       price:number;}interfaceDiscountedProductextendsProduct{
       discount:number;}interfaceProductWithReviewsextendsProduct{
       reviews:string[];}interfaceFeaturedProductextendsDiscountedProduct, 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

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

    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.

    Example

    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.

    interfaceAnimal{
       name:string;makeSound():void;}interfaceDogextendsAnimal{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

    Woof woof!
    
  • Interfaces

    An interface is a syntactical contract that an entity should conform to. In other words, an interface defines the syntax that any entity must adhere to.

    Interfaces define properties, methods, and events, which are the members of the interface. Interfaces contain only the declaration of the members. It is the responsibility of the deriving class to define the members. It often helps in providing a standard structure that the deriving classes would follow.

    Lets consider an object −

    var person ={ 
       FirstName:"Tom", 
       LastName:"Hanks",sayHi:()=>{return"Hi"}};

    If we consider the signature of the object, it could be −

    { 
       FirstName:string, 
       LastName:string,sayHi()=>string}

    To reuse the signature across objects we can define it as an interface.

    Declaring Interfaces

    The interface keyword is used to declare an interface. Here is the syntax to declare an interface −

    Syntax

    interface interface_name { 
    }
    

    Example: Interface and Objects

    interfaceIPerson{ 
       firstName:string, 
       lastName:string,sayHi:()=>string}var customer:IPerson ={ 
       firstName:"Tom",
       lastName:"Hanks", 
       sayHi:():string=>{return"Hi there"}}console.log("Customer Object ")console.log(customer.firstName)console.log(customer.lastName)console.log(customer.sayHi())var employee:IPerson ={ 
       firstName:"Jim",
       lastName:"Blakes", 
       sayHi:():string=>{return"Hello!!!"}}console.log("Employee  Object ")console.log(employee.firstName);console.log(employee.lastName);

    The example defines an interface. The customer object is of the type IPerson. Hence, it will now be binding on the object to define all properties as specified by the interface.

    Another object with following signature, is still considered as IPerson because that object is treated by its size or signature.

    On compiling, it will generate following JavaScript code.

    //Generated by typescript 1.8.10var customer ={ firstName:"Tom", lastName:"Hanks",sayHi:function(){return"Hi there";}};console.log("Customer Object ");console.log(customer.firstName);console.log(customer.lastName);console.log(customer.sayHi());var employee ={ firstName:"Jim", lastName:"Blakes",sayHi:function(){return"Hello!!!";}};console.log("Employee  Object ");console.log(employee.firstName);console.log(employee.lastName);

    The output of the above example code is as follows −

    Customer object 
    Tom 
    Hanks 
    Hi there 
    Employee  object 
    Jim  
    Blakes 
    Hello!!!
    

    Interfaces are not to be converted to JavaScript. Its just part of TypeScript. If you see the screen shot of TS Playground tool there is no java script emitted when you declare an interface unlike a class. So interfaces have zero runtime JavaScript impact.

    Interface and Objects

    Union Type and Interface

    The following example shows the use of Union Type and Interface −

    interfaceRunOptions{ 
       program:string; 
       commandline:string[]|string|(()=>string);}//commandline as string var options:RunOptions ={program:"test1",commandline:"Hello"};console.log(options.commandline)//commandline as a string array 
    options ={program:"test1",commandline:["Hello","World"]};console.log(options.commandline[0]);console.log(options.commandline[1]);//commandline as a function expression 
    options ={program:"test1",commandline:()=>{return"**Hello World**";}};var fn:any= options.commandline;console.log(fn());

    On compiling, it will generate following JavaScript code.

    //Generated by typescript 1.8.10//commandline as stringvar options ={ program:"test1", commandline:"Hello"};console.log(options.commandline);//commandline as a string array
    options ={ program:"test1", commandline:["Hello","World"]};console.log(options.commandline[0]);console.log(options.commandline[1]);//commandline as a function expression
    options ={ program:"test1",commandline:function(){return"**Hello World**";}};var fn = options.commandline;console.log(fn());

    Its output is as follows −

    Hello 
    Hello 
    World 
    **Hello World**
    

    Interfaces and Arrays

    Interface can define both the kind of key an array uses and the type of entry it contains. Index can be of type string or type number.

    Example

    interfacenamelist{[index:number]:string}var list2:namelist =["John",1,"Bran"]//Error. 1 is not type string  interfaceages{[index:string]:number}var agelist:ages; 
    agelist["John"]=15// Ok 
    agelist[2]="nine"// Error

    Interfaces and Inheritance

    An interface can be extended by other interfaces. In other words, an interface can inherit from other interface. Typescript allows an interface to inherit from multiple interfaces.

    Use the extends keyword to implement inheritance among interfaces.

    Syntax: Single Interface Inheritance

    Child_interface_name extends super_interface_name
    

    Syntax: Multiple Interface Inheritance

    Child_interface_name extends super_interface1_name, 
    super_interface2_name,,super_interfaceN_name
    

    Example: Simple Interface Inheritance

    interfacePerson{ 
       age:number}interfaceMusicianextendsPerson{ 
       instrument:string}var drummer =<Musician>{}; 
    drummer.age =27 
    drummer.instrument ="Drums"console.log("Age:  "+drummer.age)console.log("Instrument:  "+drummer.instrument)

    On compiling, it will generate following JavaScript code.

    //Generated by typescript 1.8.10var drummer ={};
    drummer.age =27;
    drummer.instrument ="Drums";console.log("Age:  "+ drummer.age);console.log("Instrument:  "+ drummer.instrument);

    Its output is as follows −

    Age: 27 
    Instrument: Drums 
    

    Example: Multiple Interface Inheritance

    interfaceIParent1{ 
       v1:number}interfaceIParent2{ 
       v2:number}interfaceChildextendsIParent1, IParent2 {}var Iobj:Child ={ v1:12, v2:23}console.log("value 1: "+this.v1+" value 2: "+this.v2)

    The object Iobj is of the type interface leaf. The interface leaf by the virtue of inheritance now has two attributes- v1 and v2 respectively. Hence, the object Iobj must now contain these attributes.

    On compiling, it will generate following JavaScript code.

    //Generated by typescript 1.8.10var Iobj ={ v1:12, v2:23};console.log("value 1: "+this.v1 +" value 2: "+this.v2);

    The output of the above code is as follows −

    value 1: 12   value 2: 23