TypeScript中未定义的协方差

问题描述

以下代码显然输入有误,但是我无法在其上输入TypeScript错误。我启用了strict以及strictNullChecksstrictFunctionTypes都是不错的选择,但是TS始终坚信此代码既精巧又精巧。

abstract class A {
    // You can pass an undefined to any A
    public abstract foo(_x: number | undefined);
}

class B extends A {
    // B is an A,but it prohibits passing in an undefined.
    // Note that if we did `x: string`,TS would flag it as
    // an error.
    public foo(x: number) {
        if (x === undefined) {
            throw new Error("Type error!");
        }
    }
}

function makeAnA(): A {
    // This typechecks correct,so B is clearly an A,in
    // TS's opinion.
    return new B();
}

function test() {
    const b = makeAnA();
    // b is a B,so this should not be possible
    b.foo(undefined);
}

这是预期的行为吗?我可以打开一些选项来将其标记为错误吗?我已经不止一次地咬过我。

解决方法

这是设计决定。所有方法参数的行为都是双变量的。这意味着就ts而言,对于方法(_x: number) => void(_x: number | number) => void的子类型(反之亦然)。这显然是不合理的。

最初,不仅方法参数是双变量的,而且所有函数签名参数也是。为了解决这个问题,在打字稿2.6中添加了strictFuctionTypes标志。来自PR

在此PR中,我们引入了--strictFunctionTypes模式,在该模式下,函数类型参数位置被反向检查而不是被双变量检查。更严格的检查适用于所有函数类型,但源于方法或构造函数声明的函数类型除外。 方法专门排除在外,以确保通用类和接口(例如Array)继续保持大多数协变关系。严格检查方法的影响将是一个重大突破,因为大量的泛型类型将变为不变(即使如此,我们可能会继续探索这种更严格的模式)。

(已突出显示)

因此,在这里,我们可以一目了然地决定让方法参数继续保持双变量相关。为了方便。如果没有这种不健全,大多数类将是不变的。例如,如果Array是不变的,则Array<Dog>不会是Array<Animal>的子类型,从而在非常基本的代码中创建各种痛苦点。

虽然绝对不相等,但是如果我们使用函数字段而不是方法(打开strictFunctionTypes),则会收到Type '(x: number) => void' is not assignable to type '(_x: number | undefined) => void'

的错误消息
abstract class A {
    // You can pass an undefined to any A
    public foo!: (_x: number | undefined) => void;
}

class B extends A {
    // Error here
    public foo: (x: number) => void = x => {
        if (x === undefined) {
            throw new Error("Type error!");
        }
    }
}

function makeAnA(): A {
    //And here 
    return new B();
}

function test() {
    const b = makeAnA();
    // b is a B,so this should not be possible
    b.foo(undefined);
}

Playground Link

注意:上面的代码仅对strictFunctionTypes给出错误,因为没有它,所有函数参数都将继续保持双变量状态。

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...