下面的代码显然输入不正确,但我不能在上面打字稿错误。 我打开了strict
,以及strictNullChecks
和strictFunctionTypes
,但 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
是 to(_x: number | number) => void
的子类型(反之亦然)。这显然是不合理的。
最初,不仅方法参数的行为是双变量的,而且所有函数签名参数的行为都是双变量的。为了解决此问题,在打字稿 2.6 中添加了strictFuctionTypes
标志。来自公关:
通过此 PR,我们引入了 --strictFunctionType 模式,在该模式下,函数类型参数位置被逆变而不是双变量检查。更严格的检查适用于所有函数类型,但源自方法或构造函数声明的函数类型除外。专门排除方法,以确保泛型类和接口(如 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);
}
游乐场链接
注意:上面的代码仅在strictFunctionTypes
时给出错误,因为没有它,所有函数参数将继续以双变量方式运行。