@@ -6655,25 +6655,31 @@ namespace ts {
6655
6655
6656
6656
function narrowTypeByValueExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
6657
6657
assumeTrue = (expr.operatorToken.kind === SyntaxKind.EqualsEqualsEqualsToken) ? assumeTrue : !assumeTrue;
6658
- let lhs = skipParenthesizedNodes(expr.left);
6658
+ let lhs = expr.left;
6659
+ // selectors is the stack of property names used to select down into a type to get the member being narrowed
6659
6660
const selectors: string[] = [];
6660
6661
while (lhs.kind !== SyntaxKind.Identifier) {
6661
6662
switch (lhs.kind) {
6663
+ case SyntaxKind.ParenthesizedExpression:
6664
+ lhs = (lhs as ParenthesizedExpression).expression;
6665
+ break;
6662
6666
case SyntaxKind.PropertyAccessExpression:
6663
- selectors.push(( lhs as PropertyAccessExpression).name.text) ;
6667
+ const name = ( lhs as PropertyAccessExpression).name.text;
6664
6668
// If a name doesn't resolve, bail
6665
- if (selectors[selectors.length - 1] === undefined) {
6669
+ if (name === undefined) {
6666
6670
return type;
6667
6671
}
6668
- lhs = skipParenthesizedNodes((lhs as PropertyAccessExpression).expression);
6672
+ selectors.push(name);
6673
+ lhs = (lhs as PropertyAccessExpression).expression;
6669
6674
break;
6670
6675
case SyntaxKind.Identifier:
6671
6676
break;
6672
6677
default:
6673
6678
// Unhandled control flow construct, don't narrow
6674
6679
return type;
6675
6680
}
6676
- };
6681
+ }
6682
+
6677
6683
const identifier = lhs as Identifier;
6678
6684
if (getResolvedSymbol(identifier) !== symbol) {
6679
6685
return type;
@@ -6683,52 +6689,59 @@ namespace ts {
6683
6689
return type;
6684
6690
}
6685
6691
6686
- // Descend into the type using the selectors we accumulated above and narrow any unions along the way
6687
- // If assumeTrue, we narrow by removing all types not compatible with the rhs type
6688
- // If not, we narrow only if the rhsType is a Value type (ie, StringLiteral) by removing all types compatible with that type (TODO)
6689
6692
if (assumeTrue) {
6690
6693
return narrowIntrospectively(type);
6691
6694
}
6692
6695
return type;
6693
6696
6697
+ /**
6698
+ * Descend into the type using the selectors we accumulated above and narrow any unions along the way
6699
+ * If assumeTrue, we narrow by removing all types not compatible with the rhs type
6700
+ * If not, we narrow only if the rhsType is a Value type (ie, StringLiteral) by removing all types compatible with that type (TODO)
6701
+ */
6694
6702
function narrowIntrospectively(type: Type) {
6695
6703
const propName = selectors.pop();
6696
6704
if (propName === undefined) {
6697
6705
// Selected all the way into the object, return the type for the property to be narrowed
6698
- if (isTypeAssignableTo (rhsType, type)) {
6706
+ if (isTypeSubtypeOf (rhsType, type)) {
6699
6707
return rhsType;
6700
6708
}
6701
6709
else {
6702
6710
return type;
6703
6711
}
6704
6712
}
6705
6713
if (type.flags & TypeFlags.Union) {
6706
- const reducedUnion = getUnionType(filter((type as UnionType).types, t => isMemberAssignable(t, rhsType, [...selectors, propName])), /*noSubtypeReduction*/ true);
6714
+ const reducedUnion = getUnionType(
6715
+ filter((type as UnionType).types, t => isMemberSubtype(t, rhsType, [...selectors, propName])),
6716
+ /*noSubtypeReduction*/ true
6717
+ );
6718
+
6707
6719
if (reducedUnion !== emptyUnionType) {
6708
- return narrowMatchingProperty (reducedUnion, propName);
6720
+ return narrowBasedOnMatchingProperty (reducedUnion, propName);
6709
6721
}
6710
6722
else {
6711
6723
return type;
6712
6724
}
6713
6725
}
6714
6726
6715
- return narrowMatchingProperty (type, propName);
6727
+ return narrowBasedOnMatchingProperty (type, propName);
6716
6728
}
6717
6729
6718
- function isMemberAssignable (type: Type, check: Type, selectors: string[]): boolean {
6730
+ function isMemberSubtype (type: Type, check: Type, selectors: string[]): boolean {
6719
6731
if (!selectors.length) {
6720
- return isTypeAssignableTo (type, check);
6732
+ return isTypeSubtypeOf (type, check);
6721
6733
}
6722
6734
const name = selectors.pop();
6723
6735
const childProp = getPropertyOfType(type, name);
6724
6736
const propType = childProp && getTypeOfSymbol(childProp);
6725
- return propType && isMemberAssignable (propType, check, selectors);
6737
+ return propType && isMemberSubtype (propType, check, selectors);
6726
6738
}
6727
6739
6728
- function narrowMatchingProperty (type: Type, name: string): Type {
6740
+ function narrowBasedOnMatchingProperty (type: Type, name: string): Type {
6729
6741
const childProp = getPropertyOfType(type, name);
6730
6742
const propType = childProp && getTypeOfSymbol(childProp);
6731
6743
const narrowedType = propType && narrowIntrospectively(propType);
6744
+
6732
6745
if (narrowedType && !isTypeIdenticalTo(propType, narrowedType)) {
6733
6746
const symbols = cloneSymbolTable(resolveStructuredTypeMembers(type as ObjectType).members);
6734
6747
const temp = createSymbol(childProp.flags, name);
0 commit comments