Let's quickly understand how Polymorphism works in TypeScript

In this lesson, we are going to look at some of the basic polymorphic principles provided by the language.

Polymorphism is a very important concept in object-oriented programming paradigm. In a nutshell, it means one object having many forms. For example, if the class Student inherits Person class, then Student has all the properties and methods Person. Hence, an instance of Student also has the type of Person since it has all the fields an instance Person would have.

Polymorphism is very important in a statically typed language. It lets you substitute values basic on this polymorphic principle. For example, a function that takes an argument of type Person would also accept a value of type Student. The same principle applies to the function return value.

This might sound familiar to you if you read the lesson on the Type System in TypeScript. In this article, we discussed structural typing and how TypeScript analyzes the shape of an object to infer types. If object A has all the properties of B, then A has the type of B (implicitly).

JavaScript is not typed languaged. Hence having polymorphism in the language doesn’t solve any major type-level problems. However, if you are familiar with instanceof operator in JavaScript, it checks if an object is an instance of a class. Let’s see how we can use it in JavaScript.

The above program is a simple JavaScript program. We have a Student class that extends Person class. The printName function takes an argument which could be anything since it’s JavaScript, we can statically analyze the code. But we can check if the person argument is an instance of the class Person.

As you can see from the result, ross and monica is an instead of Person. It’s quite obvious for ross since it’s an instance of Person but for monica, since it is an instance of Student which extends Person class, hence technically monica is an instance of Person. However, jack is not an instance of Person since it wasn’t constructed from the Person or Student class despite having the same object fields (shape).

What we learned here is that though JavaScript lacks types, polymorphism principle still exists in JavaScript and it can be useful in scenarios like these.

Interface-based Polymorphism

TypeScript is a completely different world and comparing it with JavaScript runtime would not be fair to JavaScript. In TypeScript, you have types to play with. Not only you have primitive and abstract data types that JavaScript also supports, but you can have custom types of your own such as interfaces, unions, enums, and whatnot.

As we have learned from the Interfaces lesson, an interface describes the shapes of an object. And from the Type System lesson, we have learned that TypeScript compares types of the values based on their shape. Let’s combine these two principles together.

In the above example, we have a Person interface with name and getName fields. The Student interface has the exact same field Person interface has and an extra marks field. We could have used extends keyword to inherit Person interface inside Student interface.

The printName function accepts an argument of type Person interface which means this argument expects an object with name and getName fields with types described by the Person interface.

The ross object has a type of Person interface, hence it must contain exact same fields described by the Person interface. The same goes for the monica object of the type Student interface since it extends the Person. However, jack is a plain object, hence it defines its own implicit interface.

As we can see from the result, the above program compiles and runs just fine. This is due to the fact that TypeScript’s type system is based on the shape of the values and not on what concrete type values haves. This is basically what structural typing means. Technically, concrete types do not exist in TypeScript, it is just a type system and not a language.

Hence, the printName function accepted both monica and jack as a valid argument since they have all the properties as described by the Person interface. This is very hard to implement in JavaScript since shape-based polymorphism doesn’t exist in JavaScript. TypeScript FTW!

Class-based Polymorphism

We have already seen an example of class-based polymorphism in JavaScript using the instanceof keyword. This check is not necessary in TypeScript since TypeScript won’t allow a value to be treated as an instance of a class if it doesn’t have the exact same properties described by the class.

In the above example, the printName function accepts an argument of type Person. As we learned from the classes lesson, a class in TypeScript defines an implicit interface, hence the value of the argument person is a type Person as long as the shape of that value has all the instance fields of Person. Here, the instanceof keyword is not used as one would have hoped.

Therefore, monica is a Person since Student class extends the Person class and jack is also a Person since it has all the instance fields described by the Person class. The judy object, however, is not a Person since it lacks the getFullName method.

The takeaway from this lesson is that everything in TypeScript is based on the shape of a value. Be it a unit type (_like _*'hello'*), collective type (_like _*'string'*), union type (_like _*string | number*), or interface types, everything boils down to the shape of the value.

#typescript #polymorphism