我很惊讶地发现 Typescript 允许子类在覆盖方法时在参数列表中指定更窄的类型。我不得不想象这不是疏忽,而是Typescript团队的有意选择。但是为什么?允许这种模式有什么好处?
例如:
interface Vehicle {
model: string;
}
interface TowTruck extends Vehicle {
towing: Vehicle;
}
class VehicleHelper {
stringify(vehicle: Vehicle) {
return vehicle.model;
}
}
class TowTruckHelper extends VehicleHelper {
// override parameter to be more specific than "Vehicle"
stringify(towTruck: TowTruck) {
return (
super.stringify(towTruck)
+ ' towing a '
+ super.stringify(towTruck.towing)
);
}
}
const myHelper: VehicleHelper = new TowTruckHelper();
console.log(myHelper.stringify({ model: 'Camry' }));
没有报告打字稿错误,但运行它会抛出Uncaught TypeError: Cannot read property 'model' of undefined
。
这是设计使然。克拉斯方法呈双变量相关。最初,所有函数都是双变量相关的,但对于不源自方法定义的函数签名,strictFunctionTypes
增加这一点:
更严格的检查适用于所有函数类型,但源自方法或构造声明的函数类型除外。专门排除方法,以确保泛型类和接口(如 Array(继续以协变方式关联。严格检查方法的影响将是一个更大的重大变化,因为大量的泛型类型将变得不变(即便如此,我们可能会继续探索这种更严格的模式(。
事实上,如果你使用函数字段而不是方法,unde strictFunctionType,你会得到一个错误:
interface Vehicle {
model: string;
}
interface TowTruck extends Vehicle {
towing: Vehicle;
}
class VehicleHelper {
stringify = function (vehicle: Vehicle) {
return vehicle.model;
}
}
class TowTruckHelper extends VehicleHelper {
// Property 'stringify' in type 'TowTruckHelper' is not assignable to the same property in base type 'VehicleHelper'.
stringify = function (this: TowTruckHelper, towTruck: TowTruck) {
return (
VehicleHelper.prototype.stringify.apply(this, towTruck)
+ ' towing a '
+ VehicleHelper.prototype.stringify.apply(this, towTruck.towing)
);
}
}
const myHelper: VehicleHelper = new TowTruckHelper();
console.log(myHelper.stringify({ model: 'Camry' }));
这里还有一个与您描述的标记为按预期工作的确切行为相关的问题。