Duck Typing in TypeScript


What is Duck Typing?

First, we will know what Duck Typing is. According to programmers, the circumstance where an object's type is decided by its behaviour, like methods and attributes, rather than its class is known as "duck typing".

Duck Typing in TypeScript

The usage of interfaces in TypeScript makes duck typing possible. Where interface means the set of methods and characteristics an object must have to fall under that type are described.

For example, if an interface defines the function, any object with the method called "myFunc()" may be treated as belonging to a specific kind, regardless of its class. Greater code flexibility is possible when two objects share the same behaviour and may be used interchangeably.

Duck typing emphasises assessing an object's suitability for a task by considering its methods and attributes instead of its actual type. An interface explains the set of properties and methods an object must have to be considered "duck typed" for a particular purpose.

Benefits of Duck Typing

One of duck typing's main benefits is making code more flexible and reusable. The code works with any object with the required methods and properties rather than just particular types of objects and may be used in various situations without requiring modification. Duck typing also improves code reuse by enabling the interchangeable usage of objects of diverse kinds within a single codebase.

Exmaples of Duck Typing is TypeScript

Here is an example of how to use duck typing in TypeScript −

Define an interface that represents the behaviour you want an object to have. For example −

interface Duck {
   quack(): void;
}

Create a class that implements the interface. For example −

class MallardDuck implements Duck {
   quack(): void {
      console.log("Quack!");
   }
}

Create an instance of the class and use it as the type defined by the interface.

let duck: Duck = new MallardDuck();
duck.quack(); // Output: "Quack!"

Create another class that also implements the interface −

class RubberDuck implements Duck {
   quack(): void {
      console.log("Squeak!");
   }
}

Use the new class instance as the same type defined by the interface.

let duck: Duck = new RubberDuck();
duck.quack(); // Output: "Squeak!"

As you can see, both MallardDuck and RubberDuck classes implement the Duck interface, and the duck variable can be assigned to instances of both classes. The type is determined by the behaviour (methods and properties) defined in the interface rather than the class.

It's also important to note that in typescript, you can use the typeof keyword to check the type of object in runtime and if the object has the expected method or property.

Example

In this example, the Bird and Plane classes implement the Flyable interface, which requires a fly() method. Both "duck types" can be used interchangeably in the goFly() function. The function doesn't care about the actual type of the object passed to it as long as it has a fly() method that can be called.

interface Flyable {
   fly(): void;
}

class Bird implements Flyable {
   fly(): void {
      console.log("Bird is flying");
   }
}

class Plane implements Flyable {
   fly(): void {
      console.log("Plane is flying");
   }
}

function goFly(flyable: Flyable) {
   flyable.fly();
}

let bird = new Bird();
let plane = new Plane();

goFly(bird); // Prints "Bird is flying"
goFly(plane); // Prints "Plane is flying"

On compiling, it will generate the following JavaScript code −

var Bird = /** @class */ (function () {
   function Bird() {
   }
   Bird.prototype.fly = function () {
      console.log("Bird is flying");
   };
   return Bird;
}());
var Plane = /** @class */ (function () {
   function Plane() {
   }
   Plane.prototype.fly = function () {
      console.log("Plane is flying");
   };
   return Plane;
}());
function goFly(flyable) {
   flyable.fly();
}
var bird = new Bird();
var plane = new Plane();
goFly(bird); // Prints "Bird is flying"
goFly(plane); // Prints "Plane is flying"

Output

The above code will produce the following output –

Bird is flying
Plane is flying

Example

Overall, duck typing is a powerful programming concept that allows for greater flexibility and reusability in TypeScript code by allowing objects of different types to be used interchangeably as long as they have the same methods and properties. In this example, the Driveable interface, Car and Truck classes show the same thing.

interface Driveable {
  drive(): void;
}

class Car implements Driveable {
  drive(): void {
    console.log("Car is driving");
  }
}

class Truck implements Driveable {
  drive(): void {
    console.log("Truck is driving");
  }
}

function goDrive(driveable: Driveable) {
  driveable.drive();
}

let car = new Car();
let truck = new Truck();

goDrive(car); // Prints "Car is driving"
goDrive(truck); // Prints "Truck is driving"

On compiling, it will generate the following JavaScript code −

var Car = /** @class */ (function () {
    function Car() {
    }
    Car.prototype.drive = function () {
        console.log("Car is driving");
    };
    return Car;
}());
var Truck = /** @class */ (function () {
    function Truck() {
    }
    Truck.prototype.drive = function () {
        console.log("Truck is driving");
    };
    return Truck;
}());
function goDrive(driveable) {
    driveable.drive();
}
var car = new Car();
var truck = new Truck();
goDrive(car); // Prints "Car is driving"
goDrive(truck); // Prints "Truck is driving"

Output

The above code will produce the following output –

Car is driving
Truck is driving

The main idea behind duck typing is that the code should be written to work with any object with the methods and properties it needs, rather than being written to work with specific objects. This can make the code more flexible and reusable, allowing you to use different types of objects interchangeably without changing the code.

Updated on: 21-Feb-2023

688 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements