Inversion of control in TypeScript

Inversion of control is a design pattern. I will explain this with an example.

Let's write a class Car with a drive method:

class Car {
  drive(): void {
    console.log('I am driving a car');
  }
}

We will add one more class Bus with a drive method:

class Bus {
  drive(): void {
    console.log('I am driving a bus');
  }
}

Let's assume that a driver wants to drive the Car or the Bus.

class Driver {
  startDriving(vehicle: Car | Bus): void {
    vehicle.drive();
  }
}

Instead of having a drive() method in Driver, I used a startDriving() method. It is just to indicate that Driver is a different class than Car or Bus.

In the above code, Driver class depends on Car or Bus. Let's say, we add another class Bike which has the drive method, then the code for startDriving will have to be modified like so:

startDriving(vehicle: Car | Bus | Bike): void {
  vehicle.drive();
}

With the above code, startDriving method depends on Car, Bus and Bike classes. This is "normal" dependency.

Inversion of Control or inversion of Dependency is when the startDriving method specifies that the input parameter 'vehicle' is an interface that it defines.

startDriving(vehicle: Drivable) {
  vehicle.drive();
}

In the above example, Drivable is an interface that is related to the startDriving method of the Driver class. And startDriving method uses the interface rather than concrete classes. The code for Drivable interface looks like so:

interface Drivable {
  drive(): void;
}

It is called Inversion of control or dependency because the concrete classes Car, Bus, or Bike have to implement Drivable interface so that it can be a parameter to the startDriving method. The startDriving method no longer depends on Car, Bus, Bike classes. Instead, Car, Bus, Bike classes depend (have to implement) on Drivable interface that is referenced by startDriving method.

const car = new Car();
const bus = new Bus();
const driver = new Driver();
driver.startDriving(car);
driver.startDriving(bus);

In the above code, Car and Bus implement the Drivable interface and so qualify to be parameters for startDriving method.

I will try to summarise in plain English using abstract terms. Let's say, a class A depends on class B and class C normally. But class A wants to dictate terms. So class A says, "Whoever wants to use me should implement an interface X." So, now class A and class B makes sure that they implement the interface X. So, now class B and class C depends on interface X that class A dictates. That is inversion of control in abstract terms. It is very difficult to explain this, especially in an interview.