@@ -4416,10 +4416,19 @@ namespace ts {
44164416 }
44174417 const propTypes: Type[] = [];
44184418 const declarations: Declaration[] = [];
4419+ let commonType: Type = undefined;
4420+ let hasCommonType = true;
44194421 for (const prop of props) {
44204422 if (prop.declarations) {
44214423 addRange(declarations, prop.declarations);
44224424 }
4425+ const type = getTypeOfSymbol(prop);
4426+ if (!commonType) {
4427+ commonType = type;
4428+ }
4429+ else if (type !== commonType) {
4430+ hasCommonType = false;
4431+ }
44234432 propTypes.push(getTypeOfSymbol(prop));
44244433 }
44254434 const result = <TransientSymbol>createSymbol(
@@ -4429,6 +4438,7 @@ namespace ts {
44294438 commonFlags,
44304439 name);
44314440 result.containingType = containingType;
4441+ result.hasCommonType = hasCommonType;
44324442 result.declarations = declarations;
44334443 result.isReadonly = isReadonly;
44344444 result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
@@ -7793,8 +7803,39 @@ namespace ts {
77937803 return false;
77947804 }
77957805
7796- function rootContainsMatchingReference(source: Node, target: Node) {
7797- return target.kind === SyntaxKind.PropertyAccessExpression && containsMatchingReference(source, (<PropertyAccessExpression>target).expression);
7806+ // Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared
7807+ // type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property
7808+ // a possible discriminant if its type differs in the constituents of containing union type, and if every
7809+ // choice is a unit type or a union of unit types.
7810+ function containsMatchingReferenceDiscriminant(source: Node, target: Node) {
7811+ return target.kind === SyntaxKind.PropertyAccessExpression &&
7812+ containsMatchingReference(source, (<PropertyAccessExpression>target).expression) &&
7813+ isDiscriminantProperty(getDeclaredTypeOfReference((<PropertyAccessExpression>target).expression), (<PropertyAccessExpression>target).name.text);
7814+ }
7815+
7816+ function getDeclaredTypeOfReference(expr: Node): Type {
7817+ if (expr.kind === SyntaxKind.Identifier) {
7818+ return getTypeOfSymbol(getResolvedSymbol(<Identifier>expr));
7819+ }
7820+ if (expr.kind === SyntaxKind.PropertyAccessExpression) {
7821+ const type = getDeclaredTypeOfReference((<PropertyAccessExpression>expr).expression);
7822+ return type && getTypeOfPropertyOfType(type, (<PropertyAccessExpression>expr).name.text);
7823+ }
7824+ return undefined;
7825+ }
7826+
7827+ function isDiscriminantProperty(type: Type, name: string) {
7828+ if (type && type.flags & TypeFlags.Union) {
7829+ const prop = getPropertyOfType(type, name);
7830+ if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
7831+ if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
7832+ (<TransientSymbol>prop).isDiscriminantProperty = !(<TransientSymbol>prop).hasCommonType &&
7833+ isUnitUnionType(getTypeOfSymbol(prop));
7834+ }
7835+ return (<TransientSymbol>prop).isDiscriminantProperty;
7836+ }
7837+ }
7838+ return false;
77987839 }
77997840
78007841 function isOrContainsMatchingReference(source: Node, target: Node) {
@@ -8223,7 +8264,7 @@ namespace ts {
82238264 if (isMatchingReference(reference, expr)) {
82248265 type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
82258266 }
8226- else if (isMatchingPropertyAccess (expr)) {
8267+ else if (isMatchingReferenceDiscriminant (expr)) {
82278268 type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
82288269 }
82298270 return createFlowType(type, isIncomplete(flowType));
@@ -8301,10 +8342,11 @@ namespace ts {
83018342 return cache[key] = getUnionType(antecedentTypes);
83028343 }
83038344
8304- function isMatchingPropertyAccess (expr: Expression) {
8345+ function isMatchingReferenceDiscriminant (expr: Expression) {
83058346 return expr.kind === SyntaxKind.PropertyAccessExpression &&
8347+ declaredType.flags & TypeFlags.Union &&
83068348 isMatchingReference(reference, (<PropertyAccessExpression>expr).expression) &&
8307- (declaredType.flags & TypeFlags.Union) !== 0 ;
8349+ isDiscriminantProperty (declaredType, (<PropertyAccessExpression>expr).name.text) ;
83088350 }
83098351
83108352 function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type {
@@ -8318,10 +8360,10 @@ namespace ts {
83188360 if (isMatchingReference(reference, expr)) {
83198361 return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
83208362 }
8321- if (isMatchingPropertyAccess (expr)) {
8363+ if (isMatchingReferenceDiscriminant (expr)) {
83228364 return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
83238365 }
8324- if (rootContainsMatchingReference (reference, expr)) {
8366+ if (containsMatchingReferenceDiscriminant (reference, expr)) {
83258367 return declaredType;
83268368 }
83278369 return type;
@@ -8350,13 +8392,13 @@ namespace ts {
83508392 if (isMatchingReference(reference, right)) {
83518393 return narrowTypeByEquality(type, operator, left, assumeTrue);
83528394 }
8353- if (isMatchingPropertyAccess (left)) {
8395+ if (isMatchingReferenceDiscriminant (left)) {
83548396 return narrowTypeByDiscriminant(type, <PropertyAccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
83558397 }
8356- if (isMatchingPropertyAccess (right)) {
8398+ if (isMatchingReferenceDiscriminant (right)) {
83578399 return narrowTypeByDiscriminant(type, <PropertyAccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
83588400 }
8359- if (rootContainsMatchingReference (reference, left) || rootContainsMatchingReference (reference, right)) {
8401+ if (containsMatchingReferenceDiscriminant (reference, left) || containsMatchingReferenceDiscriminant (reference, right)) {
83608402 return declaredType;
83618403 }
83628404 break;
0 commit comments