为什么 Typescript 允许使用子类参数覆盖方法



我很惊讶地发现 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' }));

这里还有一个与您描述的标记为按预期工作的确切行为相关的问题。

最新更新