这个问题非常简单,与类型守卫相关:
abstract class A {
abstract isB(): this is B;
abstract isC(): this is C;
abstract get(): number;
}
class B extends A {
isB(): this is B {
return true;
}
isC(): this is C {
return false;
}
get() {
return 5;
}
}
class C extends A {
isB(): this is B {
return false;
}
isC(): this is C {
return true;
}
get() {
return 6;
}
}
const x = new C();
if (x.isB()) {
console.log("B!")
} else {
console.log(x.get()); <--- x is inferred to never
}
正如您所看到的,在倒数第二行,x
被推断为never
,而C
非常明显A
。我非常确定this issue我已经击中了,我甚至认为了解问题是什么&#34;因为Single可以分配给Empty,代码流分析正在消除联合类型&#34;。
但是,我不了解如何利用建议的解决方法。
如果我没有B
作为基类,我确实检查过该问题,而是单独定义C
和type A = B | C
,然后说{{ 1}}。在这种情况下,打字稿甚至会在C
块中推断出else
的类型。我想用继承实现这么好的类型推理是不可能的,我现在设置的方式是什么?
答案 0 :(得分:6)
你对这个问题是正确的;您的C
和x
在结构上完全相同,因此TypeScript(包括v2.6)决定如果您已消除B
为x
的可能性,则您还消除了C
B
的可能性。这是结构型系统的特征之一;如果您希望编译器区分两种类型,它们应该是可区分的结构(具有一些不同的成员),而不仅仅是名义上(具有不同的名称)。
解决此问题的最简单方法是在C
或B
或两者的定义中添加一些区别属性。它甚至不必在运行时存在;它必须说服编译器C
和class B extends A {
readonly className: "B" // add this line
// ... no change
}
class C extends A {
readonly className: "C" // add this line
// ... no change
}
不同。这是一种可能性:
B
现在C
和className
具有不同字符串文字类型的const x = new C();
if (x.isB()) {
console.log("B!")
} else {
console.log(x.get()); //<--- x is C
}
属性。 (仅在编译时;没有任何额外的内容发送到JavaScript)。确认问题消失了:
std::move
所以,你可以这样做。现在,在2017年12月15日尚未发布的TypeScript v2.7中,将有一个change阻止编译器折叠结构相同但名义上不同的类型情况。我不确定你的上述代码是否会在没有额外属性的情况下突然开始工作,但这是可能的。所以我很快就回来看看。
希望有所帮助。祝你好运!