我定义了一个applyDamage方法来接受numberstring,但只使用number参数实现了它.当我使用基类调用这个方法时,我有不正确的行为.为什么TypeScript没有显示错误?

interface Character {
    applyDamage(value : number): number;
}
interface Humanoid extends Character {
    hp:number;
    applyDamage(value: number|string): number
}
class Monster implements Humanoid {
    hp:number = 10;
    applyDamage(v: number) {
        this.hp -= v;
        return v;
    }
}
const monster:Humanoid = new Monster();
monster.applyDamage("hello");
console.log(monster.hp); // <-- NaN

推荐答案

不幸的是,这是类型安全中的漏洞之一,因为方法参数是双变量判断的.这意味着,只要函数参数之间存在一种关系,这种关系在哪个方向上并不重要.

这里有一个链接到相关的TypeScript手册部分:Function Parameter Bivariance

其原因在引入严格函数类型(即函数签名的逆变参数类型)的PR中进行了解释,基本上是,如果对方法进行逆变判断,将导致大多数泛型类型不变(因此不能将Array<Cat>指定给Array<Animal>)

一种解决方案是避免方法签名,并尽可能使用函数签名:

interface Character {
    applyDamage: (value : number) => number;
}
interface Humanoid extends Character {
    hp:number;
    applyDamage: (value: number|string) => number
}
class Monster implements Humanoid {
    hp:number = 10;
    applyDamage(v: number) { // error
        this.hp -= v;
        return v;
    }
}
const monster:Humanoid = new Monster(); // error
monster.applyDamage("hello");
console.log(monster.hp); // <-- NaN

Playground Link

如果你想了解更多关于方差的知识,你可以观看我在it上的演示

Typescript相关问答推荐

如何将@for的结果绑定到变量?

返回对象的TypScript构造函数隐式地替换This的值来替换super(.)的任何调用者?

当使用`type B = A`时,B的类型显示为A.为什么当`type B = A时显示为`any `|用A代替?

在TypeScrip中分配回调时,参数的分配方向与回调本身相反.为什么会这样呢?

Next.js错误:不变:页面未生成

从子类构造函数推断父类泛型类型

已解决:如何使用值类型限制泛型键?

如何 bootstrap TS编译器为类型化属性`T`推断正确的类型?

常量类型参数回退类型

返回嵌套props 的可变元组

如何将类似数组的对象(字符串::匹配结果)转换为类型脚本中的数组?

tailwind css未被应用-react

使用或属性编写无限嵌套的接口

如何创建内部和外部组件都作为props 传入的包装器组件?

创建一个函数,该函数返回一个行为方式与传入函数相同的函数

在Google授权后由客户端接收令牌

推断集合访问器类型

将类型脚本函数返回类型提取到VSCode中的接口

有没有更干净的方法来更新**key of T**的值?

如何使用 AWS CDK 扩展默认 ALB 控制器策略?