@@ -4366,15 +4366,27 @@ namespace ts {
43664366 function getPropertiesOfUnionOrIntersectionType(type: UnionOrIntersectionType): Symbol[] {
43674367 for (const current of type.types) {
43684368 for (const prop of getPropertiesOfType(current)) {
4369- getPropertyOfUnionOrIntersectionType (type, prop.name);
4369+ getUnionOrIntersectionProperty (type, prop.name);
43704370 }
43714371 // The properties of a union type are those that are present in all constituent types, so
43724372 // we only need to check the properties of the first type
43734373 if (type.flags & TypeFlags.Union) {
43744374 break;
43754375 }
43764376 }
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;
43784390 }
43794391
43804392 function getPropertiesOfType(type: Type): Symbol[] {
@@ -4427,6 +4439,7 @@ namespace ts {
44274439 // Flags we want to propagate to the result if they exist in all source symbols
44284440 let commonFlags = (containingType.flags & TypeFlags.Intersection) ? SymbolFlags.Optional : SymbolFlags.None;
44294441 let isReadonly = false;
4442+ let isPartial = false;
44304443 for (const current of types) {
44314444 const type = getApparentType(current);
44324445 if (type !== unknownType) {
@@ -4444,21 +4457,20 @@ namespace ts {
44444457 }
44454458 }
44464459 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;
44494461 }
44504462 }
44514463 }
44524464 if (!props) {
44534465 return undefined;
44544466 }
4455- if (props.length === 1) {
4467+ if (props.length === 1 && !isPartial ) {
44564468 return props[0];
44574469 }
44584470 const propTypes: Type[] = [];
44594471 const declarations: Declaration[] = [];
44604472 let commonType: Type = undefined;
4461- let hasCommonType = true ;
4473+ let hasNonUniformType = false ;
44624474 for (const prop of props) {
44634475 if (prop.declarations) {
44644476 addRange(declarations, prop.declarations);
@@ -4468,25 +4480,26 @@ namespace ts {
44684480 commonType = type;
44694481 }
44704482 else if (type !== commonType) {
4471- hasCommonType = false ;
4483+ hasNonUniformType = true ;
44724484 }
4473- propTypes.push(getTypeOfSymbol(prop) );
4485+ propTypes.push(type );
44744486 }
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);
44814488 result.containingType = containingType;
4482- result.hasCommonType = hasCommonType;
4489+ result.hasNonUniformType = hasNonUniformType;
4490+ result.isPartial = isPartial;
44834491 result.declarations = declarations;
44844492 result.isReadonly = isReadonly;
44854493 result.type = containingType.flags & TypeFlags.Union ? getUnionType(propTypes) : getIntersectionType(propTypes);
44864494 return result;
44874495 }
44884496
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 {
44904503 const properties = type.resolvedProperties || (type.resolvedProperties = createMap<Symbol>());
44914504 let property = properties[name];
44924505 if (!property) {
@@ -4498,6 +4511,12 @@ namespace ts {
44984511 return property;
44994512 }
45004513
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+
45014520 /**
45024521 * Return the symbol for the property with the given name in the given type. Creates synthetic union properties when
45034522 * necessary, maps primitive types and type parameters are to their apparent types, and augments with properties from
@@ -8078,21 +8097,10 @@ namespace ts {
80788097
80798098 function isDiscriminantProperty(type: Type, name: string) {
80808099 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);
80928101 if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
80938102 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));
80968104 }
80978105 return (<TransientSymbol>prop).isDiscriminantProperty;
80988106 }
0 commit comments