@@ -4366,15 +4366,27 @@ namespace ts {
4366
4366
function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] {
4367
4367
for (const current of type.types) {
4368
4368
for (const prop of getPropertiesOfType(current)) {
4369
- getPropertyOfUnionOrIntersectionType (type, prop.name);
4369
+ getUnionOrIntersectionProperty (type, prop.name);
4370
4370
}
4371
4371
// The properties of a union type are those that are present in all constituent types, so
4372
4372
// we only need to check the properties of the first type
4373
4373
if (type.flags & TypeFlags.Union) {
4374
4374
break;
4375
4375
}
4376
4376
}
4377
- return type.resolvedProperties ? symbolsToArray(type.resolvedProperties) : emptyArray;
4377
+ const props = type.resolvedProperties;
4378
+ if (props) {
4379
+ const result: Symbol[] = [];
4380
+ for (const key in props) {
4381
+ const prop = props[key];
4382
+ // We need to filter out partial properties in union types
4383
+ if (!(prop.flags & SymbolFlags.SyntheticProperty && (<TransientSymbol>prop).isPartial)) {
4384
+ result.push(prop);
4385
+ }
4386
+ }
4387
+ return result;
4388
+ }
4389
+ return emptyArray;
4378
4390
}
4379
4391
4380
4392
function getPropertiesOfType(type: Type): Symbol[] {
@@ -4427,6 +4439,7 @@ namespace ts {
4427
4439
// Flags we want to propagate to the result if they exist in all source symbols
4428
4440
let commonFlags = (containingType.flags & TypeFlags.Intersection) ? SymbolFlags.Optional : SymbolFlags.None;
4429
4441
let isReadonly = false;
4442
+ let isPartial = false;
4430
4443
for (const current of types) {
4431
4444
const type = getApparentType(current);
4432
4445
if (type !== unknownType) {
@@ -4444,21 +4457,20 @@ namespace ts {
4444
4457
}
4445
4458
}
4446
4459
else if (containingType.flags & TypeFlags.Union) {
4447
- // A union type requires the property to be present in all constituent types
4448
- return undefined;
4460
+ isPartial = true;
4449
4461
}
4450
4462
}
4451
4463
}
4452
4464
if (!props) {
4453
4465
return undefined;
4454
4466
}
4455
- if (props.length === 1) {
4467
+ if (props.length === 1 && !isPartial ) {
4456
4468
return props[0];
4457
4469
}
4458
4470
const propTypes: Type[] = [];
4459
4471
const declarations: Declaration[] = [];
4460
4472
let commonType: Type = undefined;
4461
- let hasCommonType = true ;
4473
+ let hasNonUniformType = false ;
4462
4474
for (const prop of props) {
4463
4475
if (prop.declarations) {
4464
4476
addRange(declarations, prop.declarations);
@@ -4468,25 +4480,26 @@ namespace ts {
4468
4480
commonType = type;
4469
4481
}
4470
4482
else if (type !== commonType) {
4471
- hasCommonType = false ;
4483
+ hasNonUniformType = true ;
4472
4484
}
4473
- propTypes.push(getTypeOfSymbol(prop) );
4485
+ propTypes.push(type );
4474
4486
}
4475
- const result = <TransientSymbol>createSymbol(
4476
- SymbolFlags.Property |
4477
- SymbolFlags.Transient |
4478
- SymbolFlags.SyntheticProperty |
4479
- commonFlags,
4480
- name);
4487
+ const result = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | SymbolFlags.SyntheticProperty | commonFlags, name);
4481
4488
result.containingType = containingType;
4482
- result.hasCommonType = hasCommonType;
4489
+ result.hasNonUniformType = hasNonUniformType;
4490
+ result.isPartial = isPartial;
4483
4491
result.declarations = declarations;
4484
4492
result.isReadonly = isReadonly;
4485
4493
result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
4486
4494
return result;
4487
4495
}
4488
4496
4489
- function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol {
4497
+ // Return the symbol for a given property in a union or intersection type, or undefined if the property
4498
+ // does not exist in any constituent type. Note that the returned property may only be present in some
4499
+ // constituents, in which case the isPartial flag is set when the containing type is union type. We need
4500
+ // these partial properties when identifying discriminant properties, but otherwise they are filtered out
4501
+ // and do not appear to be present in the union type.
4502
+ function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: string): Symbol {
4490
4503
const properties = type.resolvedProperties || (type.resolvedProperties = createMap<Symbol>());
4491
4504
let property = properties[name];
4492
4505
if (!property) {
@@ -4498,6 +4511,12 @@ namespace ts {
4498
4511
return property;
4499
4512
}
4500
4513
4514
+ function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: string): Symbol {
4515
+ const property = getUnionOrIntersectionProperty(type, name);
4516
+ // We need to filter out partial properties in union types
4517
+ return property && !(property.flags & SymbolFlags.SyntheticProperty && (<TransientSymbol>property).isPartial) ? property : undefined;
4518
+ }
4519
+
4501
4520
/**
4502
4521
* Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
4503
4522
* necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
@@ -8078,21 +8097,10 @@ namespace ts {
8078
8097
8079
8098
function isDiscriminantProperty(type: Type, name: string) {
8080
8099
if (type && type.flags & TypeFlags.Union) {
8081
- let prop = getPropertyOfType(type, name);
8082
- if (!prop) {
8083
- // The type may be a union that includes nullable or primitive types. If filtering
8084
- // those out produces a different type, get the property from that type instead.
8085
- // Effectively, we're checking if this *could* be a discriminant property once nullable
8086
- // and primitive types are removed by other type guards.
8087
- const filteredType = getTypeWithFacts(type, TypeFacts.Discriminatable);
8088
- if (filteredType !== type && filteredType.flags & TypeFlags.Union) {
8089
- prop = getPropertyOfType(filteredType, name);
8090
- }
8091
- }
8100
+ const prop = getUnionOrIntersectionProperty(<UnionType>type, name);
8092
8101
if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
8093
8102
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
8094
- (<TransientSymbol>prop).isDiscriminantProperty = !(<TransientSymbol>prop).hasCommonType &&
8095
- isLiteralType(getTypeOfSymbol(prop));
8103
+ (<TransientSymbol>prop).isDiscriminantProperty = (<TransientSymbol>prop).hasNonUniformType && isLiteralType(getTypeOfSymbol(prop));
8096
8104
}
8097
8105
return (<TransientSymbol>prop).isDiscriminantProperty;
8098
8106
}
0 commit comments