@@ -3119,7 +3119,7 @@ namespace ts {
31193119 }
31203120
31213121 function resolveTupleTypeMembers(type: TupleType) {
3122- let arrayType = resolveStructuredTypeMembers(createArrayType(getUnionType(type.elementTypes, /*noDeduplication */ true)));
3122+ let arrayType = resolveStructuredTypeMembers(createArrayType(getUnionType(type.elementTypes, /*noSubtypeReduction */ true)));
31233123 let members = createTupleTypeMemberSymbols(type.elementTypes);
31243124 addInheritedMembers(members, arrayType.properties);
31253125 setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType);
@@ -3451,29 +3451,6 @@ namespace ts {
34513451 return undefined;
34523452 }
34533453
3454- // Check if a property with the given name is known anywhere in the given type. In an object
3455- // type, a property is considered known if the object type is empty, if it has any index
3456- // signatures, or if the property is actually declared in the type. In a union or intersection
3457- // type, a property is considered known if it is known in any constituent type.
3458- function isKnownProperty(type: Type, name: string): boolean {
3459- if (type.flags & TypeFlags.ObjectType && type !== globalObjectType) {
3460- const resolved = resolveStructuredTypeMembers(type);
3461- return !!(resolved.properties.length === 0 ||
3462- resolved.stringIndexType ||
3463- resolved.numberIndexType ||
3464- getPropertyOfType(type, name));
3465- }
3466- if (type.flags & TypeFlags.UnionOrIntersection) {
3467- for (let t of (<UnionOrIntersectionType>type).types) {
3468- if (isKnownProperty(t, name)) {
3469- return true;
3470- }
3471- }
3472- return false;
3473- }
3474- return true;
3475- }
3476-
34773454 function getSignaturesOfStructuredType(type: Type, kind: SignatureKind): Signature[] {
34783455 if (type.flags & TypeFlags.StructuredType) {
34793456 let resolved = resolveStructuredTypeMembers(<ObjectType>type);
@@ -4103,73 +4080,20 @@ namespace ts {
41034080 }
41044081 }
41054082
4106- function isObjectLiteralTypeDuplicateOf(source: ObjectType, target: ObjectType): boolean {
4107- let sourceProperties = getPropertiesOfObjectType(source);
4108- let targetProperties = getPropertiesOfObjectType(target);
4109- if (sourceProperties.length !== targetProperties.length) {
4110- return false;
4111- }
4112- for (let sourceProp of sourceProperties) {
4113- let targetProp = getPropertyOfObjectType(target, sourceProp.name);
4114- if (!targetProp ||
4115- getDeclarationFlagsFromSymbol(targetProp) & (NodeFlags.Private | NodeFlags.Protected) ||
4116- !isTypeDuplicateOf(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp))) {
4117- return false;
4118- }
4119- }
4120- return true;
4121- }
4122-
4123- function isTupleTypeDuplicateOf(source: TupleType, target: TupleType): boolean {
4124- let sourceTypes = source.elementTypes;
4125- let targetTypes = target.elementTypes;
4126- if (sourceTypes.length !== targetTypes.length) {
4127- return false;
4128- }
4129- for (var i = 0; i < sourceTypes.length; i++) {
4130- if (!isTypeDuplicateOf(sourceTypes[i], targetTypes[i])) {
4131- return false;
4132- }
4133- }
4134- return true;
4135- }
4136-
4137- // Returns true if the source type is a duplicate of the target type. A source type is a duplicate of
4138- // a target type if the the two are identical, with the exception that the source type may have null or
4139- // undefined in places where the target type doesn't. This is by design an asymmetric relationship.
4140- function isTypeDuplicateOf(source: Type, target: Type): boolean {
4141- if (source === target) {
4142- return true;
4143- }
4144- if (source.flags & TypeFlags.Undefined || source.flags & TypeFlags.Null && !(target.flags & TypeFlags.Undefined)) {
4145- return true;
4146- }
4147- if (source.flags & TypeFlags.ObjectLiteral && target.flags & TypeFlags.ObjectType) {
4148- return isObjectLiteralTypeDuplicateOf(<ObjectType>source, <ObjectType>target);
4149- }
4150- if (isArrayType(source) && isArrayType(target)) {
4151- return isTypeDuplicateOf((<TypeReference>source).typeArguments[0], (<TypeReference>target).typeArguments[0]);
4152- }
4153- if (isTupleType(source) && isTupleType(target)) {
4154- return isTupleTypeDuplicateOf(<TupleType>source, <TupleType>target);
4155- }
4156- return isTypeIdenticalTo(source, target);
4157- }
4158-
4159- function isTypeDuplicateOfSomeType(candidate: Type, types: Type[]): boolean {
4160- for (let type of types) {
4161- if (candidate !== type && isTypeDuplicateOf(candidate, type)) {
4083+ function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
4084+ for (var i = 0, len = types.length; i < len; i++) {
4085+ if (candidate !== types[i] && isTypeSubtypeOf(candidate, types[i])) {
41624086 return true;
41634087 }
41644088 }
41654089 return false;
41664090 }
41674091
4168- function removeDuplicateTypes (types: Type[]) {
4169- let i = types.length;
4092+ function removeSubtypes (types: Type[]) {
4093+ var i = types.length;
41704094 while (i > 0) {
41714095 i--;
4172- if (isTypeDuplicateOfSomeType (types[i], types)) {
4096+ if (isSubtypeOfAny (types[i], types)) {
41734097 types.splice(i, 1);
41744098 }
41754099 }
@@ -4194,12 +4118,14 @@ namespace ts {
41944118 }
41954119 }
41964120
4197- // We always deduplicate the constituent type set based on object identity, but we'll also deduplicate
4198- // based on the structure of the types unless the noDeduplication flag is true, which is the case when
4199- // creating a union type from a type node and when instantiating a union type. In both of those cases,
4200- // structural deduplication has to be deferred to properly support recursive union types. For example,
4201- // a type of the form "type Item = string | (() => Item)" cannot be deduplicated during its declaration.
4202- function getUnionType(types: Type[], noDeduplication?: boolean): Type {
4121+ // We reduce the constituent type set to only include types that aren't subtypes of other types, unless
4122+ // the noSubtypeReduction flag is specified, in which case we perform a simple deduplication based on
4123+ // object identity. Subtype reduction is possible only when union types are known not to circularly
4124+ // reference themselves (as is the case with union types created by expression constructs such as array
4125+ // literals and the || and ?: operators). Named types can circularly reference themselves and therefore
4126+ // cannot be deduplicated during their declaration. For example, "type Item = string | (() => Item" is
4127+ // a named type that circularly references itself.
4128+ function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
42034129 if (types.length === 0) {
42044130 return emptyObjectType;
42054131 }
@@ -4208,12 +4134,12 @@ namespace ts {
42084134 if (containsTypeAny(typeSet)) {
42094135 return anyType;
42104136 }
4211- if (noDeduplication ) {
4137+ if (noSubtypeReduction ) {
42124138 removeAllButLast(typeSet, undefinedType);
42134139 removeAllButLast(typeSet, nullType);
42144140 }
42154141 else {
4216- removeDuplicateTypes (typeSet);
4142+ removeSubtypes (typeSet);
42174143 }
42184144 if (typeSet.length === 1) {
42194145 return typeSet[0];
@@ -4230,7 +4156,7 @@ namespace ts {
42304156 function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
42314157 let links = getNodeLinks(node);
42324158 if (!links.resolvedType) {
4233- links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noDeduplication */ true);
4159+ links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction */ true);
42344160 }
42354161 return links.resolvedType;
42364162 }
@@ -4526,7 +4452,7 @@ namespace ts {
45264452 return createTupleType(instantiateList((<TupleType>type).elementTypes, mapper, instantiateType));
45274453 }
45284454 if (type.flags & TypeFlags.Union) {
4529- return getUnionType(instantiateList((<UnionType>type).types, mapper, instantiateType), /*noDeduplication */ true);
4455+ return getUnionType(instantiateList((<UnionType>type).types, mapper, instantiateType), /*noSubtypeReduction */ true);
45304456 }
45314457 if (type.flags & TypeFlags.Intersection) {
45324458 return getIntersectionType(instantiateList((<IntersectionType>type).types, mapper, instantiateType));
@@ -4813,6 +4739,30 @@ namespace ts {
48134739 return Ternary.False;
48144740 }
48154741
4742+ // Check if a property with the given name is known anywhere in the given type. In an object type, a property
4743+ // is considered known if the object type is empty and the check is for assignability, if the object type has
4744+ // index signatures, or if the property is actually declared in the object type. In a union or intersection
4745+ // type, a property is considered known if it is known in any constituent type.
4746+ function isKnownProperty(type: Type, name: string): boolean {
4747+ if (type.flags & TypeFlags.ObjectType) {
4748+ const resolved = resolveStructuredTypeMembers(type);
4749+ if (relation === assignableRelation && (type === globalObjectType || resolved.properties.length === 0) ||
4750+ resolved.stringIndexType || resolved.numberIndexType || getPropertyOfType(type, name)) {
4751+ return true;
4752+ }
4753+ return false;
4754+ }
4755+ if (type.flags & TypeFlags.UnionOrIntersection) {
4756+ for (let t of (<UnionOrIntersectionType>type).types) {
4757+ if (isKnownProperty(t, name)) {
4758+ return true;
4759+ }
4760+ }
4761+ return false;
4762+ }
4763+ return true;
4764+ }
4765+
48164766 function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
48174767 for (let prop of getPropertiesOfObjectType(source)) {
48184768 if (!isKnownProperty(target, prop.name)) {
@@ -5594,7 +5544,7 @@ namespace ts {
55945544 return getWidenedTypeOfObjectLiteral(type);
55955545 }
55965546 if (type.flags & TypeFlags.Union) {
5597- return getUnionType(map((<UnionType>type).types, getWidenedType));
5547+ return getUnionType(map((<UnionType>type).types, getWidenedType), /*noSubtypeReduction*/ true );
55985548 }
55995549 if (isArrayType(type)) {
56005550 return createArrayType(getWidenedType((<TypeReference>type).typeArguments[0]));
0 commit comments