看起来在 TypeScript 中有这样的代码绝对没问题(从编译器的角度来看):
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());
但在同一时间,我会期望一个编译错误,因为Vehicle
并Task
没有什么共同点。
而理智的用途可以通过明确的接口定义来实现:
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());
...或一个共同的父对象:
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());
是的,对于 JavaScript 来说,拥有这样的行为绝对没问题,因为根本没有类和编译器(只有语法糖),并且鸭子类型对于该语言来说是很自然的。但是 TypeScript 试图引入静态检查、类、接口等。然而,在我看来,类实例的鸭子类型看起来相当混乱且容易出错。