How to program to an interface in TypeScript

Programming to the interface is a common concept among object-oriented programmers. It provides both versatility and consistency when working with classes that have common behavior.

Since TypeScript is an object-oriented programming (OOP) language, you can take advantage of programming to the interface. An interface is an OOP feature that’s available in TypeScript yet is absent in JavaScript.

However, you must clearly understand what an interface is and how it is used before you can take advantage of the technique. Here we’ll explain the basics of interfaces in OOP programming, and then how how to implement the technique of programming to the interface.

What is an interface?

An interface is a programming structure that declares properties and methods without implementation. Once an interface is declared it is implemented in a class. In other words, an interface declares what a class is supposed to do in terms of properties and methods, but not how the properties and methods work. Rather, a class implements the behavior an interface represents.

The following is an example of an interface programming in TypeScript named IAnimal.

export interface IAnimal {
  sound: string;
  legs: number;
  speak();
}

Notice that the interface IAnimal defines two properties, sound and legs. Also, the interface defines a method, speak(). Any class that implements the interface must provide behavior for those properties and methods.

Interface implementation

The following example of TypeScript code declares a class named Pig. The class Pig implements the interface IAnimal.

export class Pig implements IAnimal {
  legs = 4;
  sound = 'Oink';
  name = 'Porky';
  speak() {
    return this.sound;
  }
}

Notice above that that the code assigns values to the properties sound and legs. Also, the code gives the speak() method behavior, in this case to return the value assigned to the property sound. In addition, a property named name is added to the class Pig. This is OK because a class that implements an interface can declare additional properties and methods. These additions are supported by the class, but not the interface. This is a subtle distinction, the implication of which will be explained in a moment.

The TypeScript code below is an example of another class that implements the IAnimal interface. In this case the class is named Chicken.

export class Chicken implements IAnimal {
  legs = 2;
  sound = 'Cluck';
  eggColor = 'Brown';
  speak() {
    return this.sound;
  }
}

Notice that the class Chicken implements behavior for the legs and sound properties of the IAnimal interface. TheChicken class also provides behavior for the speak() method, as well as an added property, eggColor. As with the Pig class, it is permissible to add properties and methods to a class that implements an interface.

How to program to an interface

Now it’s time to get the notion of how to program to the interface.

The following TypeScript code creates an array of objects that support the IAnimal interface and then adds classes the implement the IAnimal interface to the array:

// Create an array that will hold classes that
// support the IAnimal interface
const animals = new Array<IAnimal>();

// Create an instance of the Pig class and add
// it to the array
animals.push(new Pig());


// Create an instance of the Chicken class and add
// it to the array
animals.push(new Chicken());

// Traverse the array …
animals.forEach(animal => {

  // call the legs property and execute the speak() method
  // on each item in the array. Remember, each item in the
  // array is an IAnimal interface
  console.log(
    'The animal has ${animal.legs} legs and says: ${animal.speak()}'
  );
});

When the code executes, here is the result:

The animal has 4 legs and says: Oink
The animal has 2 legs and says: Cluck

The important thing to understand about the code examples shown above is that all interactions with the instances of the Pig and Chicken classes are conducted by way of the IAnimal interface. The code does not directly call the instance of the class, only the interface that the class supports. This is the essence of programming to the interface.

The benefit of programming to the interface is that I can work with any number of classes in the same manner as long as they all support the same interface. Remember, in the example above, the following code…

    'The animal has ${animal.legs} legs and says: ${animal.speak()}'

… is repeatedly called. Yet the result of each call is different because the implementation of each instance of the interface in the given class is different.

That’s the trick to working with programming to the interface. First, declare the interface(s) you want your classes to support. Then implement the behavior in the class accordingly.