TypeScript Interfaces Tutorial

In this TypeScript tutorial we learn about interfaces that have no member implementation, forcing developers to follow a specific design and allowing us to build loosely coupled applications.

We cover defining and implementing one or more interfaces, as well as how to implement the members of an interface.

What is an interface

An interface is like a class whose members aren’t implemented.

If you remember from our tutorial on composition, we spoke about favouring a has-a relationship over an is-a relationship. To recap quickly:

  • Inheritance can be abused, which may lead to a large, fragile hierarchy of classes.
  • Inheritance makes our classes tightly coupled and dependent on each other. We want them to be as loosely coupled as possible.

Interfaces allow us to have easy has-a relationships between classes. Instead of defining a class for each relationship, we define interfaces.

note Even though an interface has nothing to do with inheritance, it does allow for polymorphic behavior in a similar way.

How to define an interface

Interfaces are define the same way as classes, except we use the interface keyword instead of the class keyword.

Interface members aren’t implemented. We don’t initialize properties with values, or add code blocks to methods, although they can contain parameters.

Syntax:
interface InterfaceName {

  property: type
  method(parameter: type)
}

How to implement an interface

Interfaces aren’t inherited, but rather implemented. We implement an interface with the implements keyword.

Syntax:
interface InterfaceName {}

class ClassName implements InterfaceName {}
Example:
// Interface
interface Flyable {

  fly(speed: number): void
}

// Implementations
class Sparrow implements Flyable {

	fly(speed: number): void {
		console.log("Sparrow flying at a speed of " + speed)
	}
}

class Plane implements Flyable {

	fly(speed: number): void {
		console.log("Plane flying at a speed of " + speed)
	}
}

// Usage
let sparrow1 = new Sparrow()
sparrow1.fly(10)

var plane1 = new Plane()
plane1.fly(100)

Both Sparrow and Plane classes implement the Flyable interface and both have to implement their own versions of the fly() method.

It’s similar to what we did in the lesson on composition but with interfaces, we force other developers to follow our design. If a method exists in the interface, a developer has to implement a version of it.

If a class does not implement their own version of the methods in the interface, the compiler will raise an error.

Example:
interface Flyable {

  fly(speed: number): void
}

class Sparrow implements Flyable {}

In the example above, we don’t have the fly() method in our Sparrow class, so the compiler raises an error.

Output:
main.ts:6:7 - error TS2420: Class 'Sparrow' incorrectly implements interface 'Flyable'.
  Property 'fly' is missing in type 'Sparrow' but required in type 'Flyable'.

6 class Sparrow implements IFlyable {}
        ~~~~~~~

  main.ts:3:3
    3   fly(speed);
        ~~~~~~~~~~~
    'fly' is declared here.

How to implement multiple interfaces

Another benefit of interfaces is that we can implement more than one.

note Interfaces aren’t used for multiple inheritance, it’s just a benefit we get when using an interface.

To implement multiple interfaces, we separate them with a comma.

Syntax:
interface InterfaceName1 {}

interface InterfaceName2 {}

class ClassName implements InterfaceName1, InterfaceName2 {}
Example:
interface Flyable {

  fly(speed: number): void
}
interface Hoppable {

  hop(speed: number): void
}

class Sparrow implements Flyable, Hoppable {

	fly(speed: number): void {
		console.log("Sparrow flying at a speed of " + speed)
	}
  hop(speed: number): void {
    console.log("Sparrow hopping at " + speed + " hops per minute")
  }
}

var sparrow1 = new Sparrow()
sparrow1.fly(10)
sparrow1.hop(87)

In the example above, we create another interface called Hoppable . Because a sparrow can both fly and hop around, it can implement both interfaces.

If we decided to add a ‘Bunny’ class, all we need to do is implement the ‘IHoppable’ interface. Nothing breaks or needs to be changed because it’s all loosely coupled.

Summary: Points to remember

  • Interfaces allow for a loosely coupled has-a relationship between classes, similar to composition.
  • Interfaces force a developer to provide their own implementation.
  • Classes can implement multiple interfaces.