Mastering TypeScript 3
上QQ阅读APP看书,第一时间看更新

Implementing interfaces

Before we continue exploring classes, let's take a look at the relationship between classes and interfaces. A class is a definition of an object, including its properties and functions. An interface is the definition of a custom type, also including its properties and functions. The only real difference is that classes must implement functions and properties, whereas interfaces only describe them. This allows us to use interfaces to describe some common behavior of a group of classes, and then write code that will work with any one of these classes. Consider the following class definitions:

class ClassA { 
    print() { console.log('ClassA.print()') }; 
} 
 
class ClassB { 
    print() { console.log(`ClassB.print()`)}; 
} 

Here we have class definitions for two classes, ClassA and ClassB. Both of these classes just have a print function. Suppose that we wanted to write some code that did not really care what type of class we used, all it cared about was whether the class had a print function. Instead of writing a complex class that needed to deal with instances of both ClassA and ClassB, we can easily create an interface describing the behavior we need, as follows:

interface IPrint { 
    print() : void; 
} 
 
function printClass( a : IPrint ) { 
    a.print(); 
} 

Here, we have created an interface named IPrint to describe the attributes of an object that we need within the printClass function. This interface has a single function named print. Therefore, any variable that is passed in as an argument to the printClass function must itself have a function named print.

We can now modify our class definitions to ensure that they can both be used by the printClass function, as follows:

class ClassA implements IPrint { 
    print() { console.log('ClassA.print()') }; 
} 
 
class ClassB implements IPrint { 
    print() { console.log(`ClassB.print()`)}; 
}

Our class definitions now use the implements keyword to implement the IPrint interface. This allows us to use both classes within the printClass function, as follows:

let classA = new ClassA(); 
let classB = new ClassB(); 
 
printClass(classA); 
printClass(classB); 

Here, we are creating instances of ClassA and ClassB, and then calling the same printClass function with both instances. Because the printClass function is written to accept any object that implements the IPrint interface, it will work correctly with both class types.

Interfaces, therefore, are a way of describing class behavior. Interfaces can also be seen as a type of contract that classes must implement, if they are expected to provide certain behaviors.