这个问题非常简单,与类型保护有关:
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作为基类,而是独立地定义B和C,然后说type A = B | C,我就检查这个问题是否会出现。在这种情况下,类型记录甚至推断出C块中的类型。我想用继承实现这么好的类型推断是不可能的,就像我现在设置它的方式一样?
发布于 2017-12-16 00:59:56
在这个问题上,您是正确的;您的B和C在结构上是相同的,因此TypeScript (直到并包括v2.6)决定,如果您消除了x是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属性。(仅在编译时,不向JavaScript发出任何额外的信号)。确认问题解决了:
const x = new C();
if (x.isB()) {
console.log("B!")
} else {
console.log(x.get()); //<--- x is C
}所以你可以这么做。现在,在截至2017年12月15日尚未发布的TypeScript v2.7中,将有一个变化来防止编译器在某些情况下崩溃--结构相同但名义上不同的类型。我不确定您的上述代码是否会在没有额外属性的情况下突然开始工作,但这是可能的。我想很快就回来看看吧。
希望这能有所帮助。祝好运!
https://stackoverflow.com/questions/47841211
复制相似问题