typescript - 这种比较似乎是无意的,因为类型 'this' 和 'Derived' 没有重叠
我有一个类Base和一个从它继承的类Derived。Base有一个方法,它以Derived作为参数并将this与它进行比较:
class Base {
foo(bar: Derived): void {
if(this === bar){
return;
}
//Do stuff
}
}
class Derived extends Base {
baz(): void {}
}
操场链接
当我尝试编译它时,我收到这个错误:
error TS2367: This comparison appears to be unintentional because the types
'this' and 'Derived' have no overlap.
3 if(this === bar){
~~~~~~~~~~~~
我不明白为什么会出现这个错误,this 和 Derived 都是 Base 类型,所以它们确实有重叠。如果我执行 const x=new Derived(); x.foo(x); 它会进入 if 块,因此这个比较并不像编译器认为的那样无用。
有趣的是,如果我删除 baz() 方法,使 Derived 的主体为空,错误就会消失(但这并不能解决我的问题,因为一个空主体的类意义不大)。
我为什么会收到这个错误,我该如何修复它?
答案#1
错误信息的措辞可能会导致误解。将“没有重叠”理解为“无法同时具有 this 类型和 Derived 类型的值”是合理的,但显然这并不正确。以前的错误信息甚至更糟,会显示“此比较始终为 false”,而不是“这似乎是无意的”。这一点后来有所更改,见 microsoft/TypeScript#27910,但关于“没有重叠”的困惑依然存在,人们也提交了相关问题,例如 microsoft/TypeScript#60583、microsoft/TypeScript#44645 和 microsoft/TypeScript#37155。其中一些被归类为错误,但通常在 TypeScript 团队给出解释时,会说明错误的存在本身并不是 bug,而是 信息 可以改进。
实际上发生的情况是,TypeScript 有各种启发式方法来判断使用像 === 这样的比较运算符是否可能是有意的或不太可能是有意的。粗略地说,规则是:如果 expr1 的类型与 expr2 的类型兼容,或者反之,则允许 expr1 === expr2。这里的“兼容”意思类似于“可赋值给”、“子类型”或“类型收窄”。细节并不是特别重要。
在你的情况下,你不能将 this 赋值为类型为 Derived 的值,也不能将 bar 赋值为类型为 this 的值,因此两者都不能相互赋值。所以 TypeScript 将此视为你的比较可能是无意的。还有类似的情况,开发者会很高兴收到该错误信息,因为他们确实写错了东西。如 microsoft/TypeScript#44645 的评论中所提到的:
这些就是那种检查类型,当我们稍微加强它们时,就能在实际代码中发现大量的实际漏洞。
使用
===可能会出现各种错误,这些错误不是可证明的总是为假,但很可能不是故意为之。在奇怪的代码中产生少量误报以在有问题的代码中获得真实命中,这是所有类型检查器故意做出的权衡。
在 TypeScript 将你的比较标记为可能非故意,但你确实是有意为之的情况下,你可以将比较的其中一方 拓宽 为另一方的超类型。如你所说,this 和 Derived 都是 Base 类型。所以将任意一方拓宽为 Base,错误就会消失:
if (this as Base === bar) { }
if (this === bar as Base) { }
最简单的方法是使用类型断言来拓宽类型,但由于类型断言也可能不安全地缩小类型,你可能更愿意避免使用或辅以更安全的方法:
const thisBase: Base = this; if (thisBase === bar) { }
if (this satisfies Base as Base === bar) { }
不过,无论如何,你都必须做一些小动作来让 TypeScript 相信你是故意这么做的。这并不理想,但对于每一个“哦,拜托,这显然是故意的”,实际上都有相当多的“哦,这其实是个错误”。这类事情本质上总是启发式的,由于无法完美捕捉开发者的意图,它们能做的最好就是尝试找到一个位置,使得这种权衡能够最大化平均开发者体验。
代码的 Playground 链接
