Interfaces

Interfaces are a key piece of creating large-scale software applications. They are a way of representing complex types about any object. Despite their usefulness they have absolutely no runtime consequences because JavaScript does not include any sort of runtime type checking. Interfaces are analyzed at compile time and then omitted from the resulting JavaScript. Interfaces create a contract for developers to use when developing new objects or writing methods to interact with existing ones. Interfaces are named types that contain a list of members. Let's look at an example of an interface:

interface IPoint {
    x: number;
    y: number;
}

As you can see we use the interface keyword to start the interface declaration. Then we give the interface a name that we can easily reference from our code.

Note

Interfaces can be named anything, for example, foo or bar, however, a simple naming convention will improve the readability of the code. Throughout this book, interfaces will be given the format I<name> and object types will just use <name>, for example, IFoo and Foo.

The interfaces' declaration body contains just a list of members and functions and their types. Interface members can only be instance members of an object. Using the static keyword in an interface declaration will result in a compile error.

Interfaces have the ability to inherit from base types. This interface inheritance allows us to extend existing interfaces into a more enhanced version as well as merge separate interfaces together. To create an inheritance chain, interfaces use the extends clause. The extends clause is followed by a comma-separated list of types that the interface will merge with.

interface IAdder {
    add(arg1: number, ...args: number[]): number;
}
interface ISubtractor {
    subtract(arg1: number, ...args: number[]): number;
}
interface ICalculator extends IAdder, ISubtractor {
    multiply(arg1: number, ...args: number[]): number;
    divide(arg1: number, arg2: number): number;
}

Here, we see three interfaces:

  • IAdder, which defines a type that must implement the add method that we wrote earlier
  • ISubtractor, which defines a new method called subtract that any object typed with ISubtractor must define
  • ICalculator, which extends both IAdder and ISubtractor as well as defining two new methods that perform operations a calculator would be responsible for, which an adder or subtractor wouldn't perform

These interfaces can now be referenced in our code as type parameters or type declarations. Interfaces cannot be directly instantiated and attempting to reference the members of an interface by using its type name directly will result in an error. In the following function declaration the ICalculator interface is used to restrict the object type that can be passed to the function. The compiler can now examine the function body and infer all of the type information associated with the calculator parameter and warn us if the object used does not implement this interface.

function performCalculations(calculator: ICalculator, num1, num2) {
    calculator.add(num1, num2);
    calculator.subtract(num1, num2);
    calculator.multiply(num1, num2);
    calculator.divide(num1, num2);
    return true;
}

The last thing that you need to know about interface definitions is that their declarations are open-ended and will implicitly merge together if they have the same type name. Our ICalculator interface could have been split into two separate declarations with each one adding its own list of base types and its own list of members. The resulting type definition from the following declaration is equivalent to the declaration we saw previously:

interface ICalculator extends IAdder {
    multiply(arg1: number, ...args: number[]): number;
}
interface ICalculator extends ISubtractor {
    divide(arg1: number, arg2: number): number;
}

Creating large scale applications requires code that is flexible and reusable. Interfaces are a key component of keeping TypeScript as flexible as plain JavaScript, yet allow us to take advantage of the type checking provided at compile time. Your code doesn't have to be dependent on existing object types and will be ready for any new object types that might be introduced. The TypeScript compiler also implements a duck typing system that allows us to create objects on the fly while keeping type safety. The following example shows how we can pass objects that don't explicitly implement an interface but contain all of the required members to a function:

function addPoints(p1: IPoint, p2: IPoint): IPoint {
    var x = p1.x + p2.x;
    var y = p1.y + p2.y;
    return { x: x, y: y }
}
//Valid
var newPoint = addPoints({ x: 3, y: 4 }, { x: 5, y: 1 });
//Error
var newPoint2 = addPoints({ x: 1 }, { x: 4, y: 3 });
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset