我有一个基本接口,其中至少需要两个属性之一。
MyType
当我在检查someProp
不为空后尝试使用condProp1
并且在检查condProp2
或someProp
的真实性之后时,编译器仍然认为null
可能是function doSomething(myType: MyType) {
if (myType.someProp && (
myType.condProp1 && myType.someProp.subProp === 'foo' ||
myType.condProp2 && myType.someProp.subProp === 'bar')) {
console.log("It's true, isn't it? It's really true.");
}
}
。
doSomething()
在myType.someProp.subProp
中,someProp
给出了一个编译时错误,指出null
可能是MyType
。如果我在函数签名中将Base
替换为// Compiles fine
function doSomethingBase(myType: Base) {
if (myType.someProp && (
myType.condProp1 && myType.someProp.subProp === 'foo' ||
myType.condProp2 && myType.someProp.subProp === 'bar')) {
console.log("It's true, isn't it? It's really true.");
}
}
,则不会发生这种情况。
myType.someProp.subProp === 'foo' && myType.condProp1
将顺序切换为!
可作为第一行而不是第二行的解决方法。知道为什么会发生这种情况或如何解决(我可以使用{{1}},但不必这样做)吗?好像是编译器中的错误。
我正在使用TSC版本3.5.3。 TypeScript playground
答案 0 :(得分:1)
我怀疑这实际上是一个编译器错误;我不太了解内部结构,但是我怀疑编译器正在将初始保护子句评估为Base
类型,然后将内部子句评估为WithPropX
,并且丢失了有关作为WithPropX
的myType已经被防止使用null someProp。
您可以通过反转检查的顺序来解决此问题,因此更具体的类型推断会在条件中更早地发生(然后,在编译器知道myType是{{1 }},而不是WithPropX
)
Base
This issue似乎与您在这里观察到的情况有关。
答案 1 :(得分:0)
我喜欢@Chris Helad说:
我怀疑编译器正在将初始的保护子句评估为Base类型,然后将内部子句评估为WithPropX,并且丢失了信息,即myType as WithPropX已经被保护为null someProp。
因此,编译器可能会这样工作:
function doSomething(myType: MyType) {
if (myType.someProp && ( // myType is type of WithProp1 | WithProp2 AND someProp is defined
myType.condProp1 && myType.someProp.subProp === 'foo' || // condProp1 is defined, so, myType is typeof WithProp1 (forgot that someProp is defined)
myType.condProp2 && myType.someProp.subProp === 'bar')) { // condProp2 is defined, so, myType is typeof WithProp2 (forgot that someProp is defined)
console.log(`It's true, isn't it? It's really true.`);
}
}
要击败它,您可以定义一个简单的type guard来告诉编译器实际类型:
function hasProp(t: MyType): t is MyType & {someProp: NonNullable<MyType['someProp']>} {
return !!t.someProp;
}
function doSomething(myType: MyType) {
if (hasProp(myType) && (
myType.condProp1 && myType.someProp.subProp === 'foo' ||
myType.condProp2 && myType.someProp.subProp === 'bar')) {
console.log(`It's true, isn't it? It's really true.`);
}
}