@@ -4416,10 +4416,19 @@ namespace ts {
4416
4416
}
4417
4417
const propTypes: Type[] = [];
4418
4418
const declarations: Declaration[] = [];
4419
+ let commonType: Type = undefined;
4420
+ let hasCommonType = true;
4419
4421
for (const prop of props) {
4420
4422
if (prop.declarations) {
4421
4423
addRange(declarations, prop.declarations);
4422
4424
}
4425
+ const type = getTypeOfSymbol(prop);
4426
+ if (!commonType) {
4427
+ commonType = type;
4428
+ }
4429
+ else if (type !== commonType) {
4430
+ hasCommonType = false;
4431
+ }
4423
4432
propTypes.push(getTypeOfSymbol(prop));
4424
4433
}
4425
4434
const result = <TransientSymbol>createSymbol(
@@ -4429,6 +4438,7 @@ namespace ts {
4429
4438
commonFlags,
4430
4439
name);
4431
4440
result.containingType = containingType;
4441
+ result.hasCommonType = hasCommonType;
4432
4442
result.declarations = declarations;
4433
4443
result.isReadonly = isReadonly;
4434
4444
result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
@@ -7793,8 +7803,39 @@ namespace ts {
7793
7803
return false;
7794
7804
}
7795
7805
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;
7798
7839
}
7799
7840
7800
7841
function isOrContainsMatchingReference(source: Node, target: Node) {
@@ -8223,7 +8264,7 @@ namespace ts {
8223
8264
if (isMatchingReference(reference, expr)) {
8224
8265
type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
8225
8266
}
8226
- else if (isMatchingPropertyAccess (expr)) {
8267
+ else if (isMatchingReferenceDiscriminant (expr)) {
8227
8268
type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
8228
8269
}
8229
8270
return createFlowType(type, isIncomplete(flowType));
@@ -8301,10 +8342,11 @@ namespace ts {
8301
8342
return cache[key] = getUnionType(antecedentTypes);
8302
8343
}
8303
8344
8304
- function isMatchingPropertyAccess (expr: Expression) {
8345
+ function isMatchingReferenceDiscriminant (expr: Expression) {
8305
8346
return expr.kind === SyntaxKind.PropertyAccessExpression &&
8347
+ declaredType.flags & TypeFlags.Union &&
8306
8348
isMatchingReference(reference, (<PropertyAccessExpression>expr).expression) &&
8307
- (declaredType.flags & TypeFlags.Union) !== 0 ;
8349
+ isDiscriminantProperty (declaredType, (<PropertyAccessExpression>expr).name.text) ;
8308
8350
}
8309
8351
8310
8352
function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type {
@@ -8318,10 +8360,10 @@ namespace ts {
8318
8360
if (isMatchingReference(reference, expr)) {
8319
8361
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
8320
8362
}
8321
- if (isMatchingPropertyAccess (expr)) {
8363
+ if (isMatchingReferenceDiscriminant (expr)) {
8322
8364
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
8323
8365
}
8324
- if (rootContainsMatchingReference (reference, expr)) {
8366
+ if (containsMatchingReferenceDiscriminant (reference, expr)) {
8325
8367
return declaredType;
8326
8368
}
8327
8369
return type;
@@ -8350,13 +8392,13 @@ namespace ts {
8350
8392
if (isMatchingReference(reference, right)) {
8351
8393
return narrowTypeByEquality(type, operator, left, assumeTrue);
8352
8394
}
8353
- if (isMatchingPropertyAccess (left)) {
8395
+ if (isMatchingReferenceDiscriminant (left)) {
8354
8396
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
8355
8397
}
8356
- if (isMatchingPropertyAccess (right)) {
8398
+ if (isMatchingReferenceDiscriminant (right)) {
8357
8399
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
8358
8400
}
8359
- if (rootContainsMatchingReference (reference, left) || rootContainsMatchingReference (reference, right)) {
8401
+ if (containsMatchingReferenceDiscriminant (reference, left) || containsMatchingReferenceDiscriminant (reference, right)) {
8360
8402
return declaredType;
8361
8403
}
8362
8404
break;
0 commit comments