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.