Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why duck typing is allowed for classes in TypeScript

Looks like in TypeScript it's absolutely fine (from the compiler perspective) to have such code:

class Vehicle {     public run(): void { console.log('Vehicle.run'); } }  class Task {     public run(): void { console.log('Task.run'); } }  function runTask(t: Task) {     t.run(); }  runTask(new Task()); runTask(new Vehicle()); 

But at the same time I would expect a compilation error, because Vehicle and Task don't have anything in common.

And sane usages can be implemented via explicit interface definition:

interface Runnable {     run(): void; }  class Vehicle implements Runnable {     public run(): void { console.log('Vehicle.run'); } }  class Task implements Runnable {     public run(): void { console.log('Task.run'); } }  function runRunnable(r: Runnable) {     r.run(); }  runRunnable(new Task()); runRunnable(new Vehicle()); 

... or a common parent object:

class Entity {     abstract run(): void; }  class Vehicle extends Entity {     public run(): void { console.log('Vehicle.run'); } }  class Task extends Entity {     public run(): void { console.log('Task.run'); } }  function runEntity(e: Entity) {     e.run(); }  runEntity(new Task()); runEntity(new Vehicle()); 

And yes, for JavaScript it's absolutely fine to have such behaviour, because there is no classes and no compiler at all (only syntactic sugar) and duck typing is natural for the language. But TypeScript tries to introduce static checks, classes, interfaces, etc. However duck typing for class instances looks rather confusing and error-prone, in my opinion.

like image 425
Ivan Velichko Avatar asked Feb 16 '18 15:02

Ivan Velichko


People also ask

Does TypeScript support duck typing?

TypeScript uses the duck-typing method to compare one object with other objects by checking that both objects have the same type matching names or not. It means we cannot change the signature of a variable.

What is duck typing in TS?

The duck-typing technique in TypeScript is used to compare two objects by determining if they have the same type matching properties and objects members or not. For example, if we assign an object with two properties and a method and the second object is only assigned with two properties.

What kind of typing does TypeScript use?

In particular, TypeScript is strongly typed — that is, variables and other data structures can be declared to be of a specific type, like a string or a boolean, by the programmer, and TypeScript will check the validity of their values. This isn't possible in JavaScript, which is loosely typed.

Is TypeScript structural typing?

TypeScript is a Structural Type System. A structural type system means that when comparing types, TypeScript only takes into account the members on the type. This is in contrast to nominal type systems, where you could create two types but could not assign them to each other.


1 Answers

This is the way structural typing works. Typescript has a structural type system to best emulate how Javscript works. Since Javascript uses duck typing, any object that defines the contract can be used in any function. Typescript just tries to validate duck typing at compile time instead of at runtime.

Your problem will however only manifest for trivial classes, as soon as you add privates, classes become incompatible even if they have the same structure:

class Vehicle {     private x: string;     public run(): void { console.log('Vehicle.run'); } }  class Task {     private x: string;     public run(): void { console.log('Task.run'); } }  function runTask(t: Task) {     t.run(); }  runTask(new Task()); runTask(new Vehicle()); // Will be a compile time error 

This behavior also allows you to not explicitly implement interfaces, for example you function could define the interface for the parameter inline, and any class that satisfies the contract will be compatible even if they don't explicitly implement any interface:

function runTask(t: {  run(): void }) {     t.run(); }  runTask(new Task()); runTask(new Vehicle()); 

On a personal note, coming from C# this was seemed insane at first, but when it comes to extensibility this way of type checking allows for much greater flexibility, once you get used to it you will see the benefits.

like image 168
Titian Cernicova-Dragomir Avatar answered Oct 13 '22 13:10

Titian Cernicova-Dragomir