@@ -3975,26 +3975,59 @@ namespace ts {
3975
3975
}
3976
3976
}
3977
3977
3978
- function isSubtypeOfAny(candidate: Type, types: Type[]): boolean {
3978
+ function isObjectLiteralTypeDuplicateOf(source: ObjectType, target: ObjectType): boolean {
3979
+ let sourceProperties = getPropertiesOfObjectType(source);
3980
+ let targetProperties = getPropertiesOfObjectType(target);
3981
+ if (sourceProperties.length !== targetProperties.length) {
3982
+ return false;
3983
+ }
3984
+ for (let sourceProp of sourceProperties) {
3985
+ let targetProp = getPropertyOfObjectType(target, sourceProp.name);
3986
+ if (!targetProp ||
3987
+ getDeclarationFlagsFromSymbol(targetProp) & (NodeFlags.Private | NodeFlags.Protected) ||
3988
+ !isTypeDuplicateOf(getTypeOfSymbol(sourceProp), getTypeOfSymbol(targetProp))) {
3989
+ return false;
3990
+ }
3991
+ }
3992
+ return true;
3993
+ }
3994
+
3995
+ function isTypeDuplicateOf(source: Type, target: Type): boolean {
3996
+ if (source === target) {
3997
+ return true;
3998
+ }
3999
+ if (source.flags & TypeFlags.Undefined || source.flags & TypeFlags.Null && !(target.flags & TypeFlags.Undefined)) {
4000
+ return true;
4001
+ }
4002
+ if (source.flags & TypeFlags.ObjectLiteral && target.flags & TypeFlags.ObjectType) {
4003
+ return isObjectLiteralTypeDuplicateOf(<ObjectType>source, <ObjectType>target);
4004
+ }
4005
+ if (isArrayType(source) && isArrayType(target)) {
4006
+ return isTypeDuplicateOf((<TypeReference>source).typeArguments[0], (<TypeReference>target).typeArguments[0]);
4007
+ }
4008
+ return isTypeIdenticalTo(source, target);
4009
+ }
4010
+
4011
+ function isTypeDuplicateOfSomeType(candidate: Type, types: Type[]): boolean {
3979
4012
for (let type of types) {
3980
- if (candidate !== type && isTypeSubtypeOf(getRegularTypeOfObjectLiteral( candidate) , type)) {
4013
+ if (candidate !== type && isTypeDuplicateOf( candidate, type)) {
3981
4014
return true;
3982
4015
}
3983
4016
}
3984
4017
return false;
3985
4018
}
3986
4019
3987
- function removeSubtypes (types: Type[]) {
4020
+ function removeDuplicateTypes (types: Type[]) {
3988
4021
let i = types.length;
3989
4022
while (i > 0) {
3990
4023
i--;
3991
- if (isSubtypeOfAny (types[i], types)) {
4024
+ if (isTypeDuplicateOfSomeType (types[i], types)) {
3992
4025
types.splice(i, 1);
3993
4026
}
3994
4027
}
3995
4028
}
3996
4029
3997
- function containsTypeAny(types: Type[]) {
4030
+ function containsTypeAny(types: Type[]): boolean {
3998
4031
for (let type of types) {
3999
4032
if (isTypeAny(type)) {
4000
4033
return true;
@@ -4017,27 +4050,28 @@ namespace ts {
4017
4050
return type1.id - type2.id;
4018
4051
}
4019
4052
4020
- // The noSubtypeReduction flag is there because it isn't possible to always do subtype reduction. The flag
4021
- // is true when creating a union type from a type node and when instantiating a union type. In both of those
4022
- // cases subtype reduction has to be deferred to properly support recursive union types. For example, a
4023
- // type alias of the form "type Item = string | (() => Item)" cannot be reduced during its declaration.
4024
- function getUnionType(types: Type[], noSubtypeReduction?: boolean): Type {
4053
+ // The noDeduplication flag exists because it isn't always possible to deduplicate the constituent types.
4054
+ // The flag is true when creating a union type from a type node and when instantiating a union type. In
4055
+ // both of those cases subtype deduplication has to be deferred to properly support recursive union types.
4056
+ // For example, a type alias of the form "type Item = string | (() => Item)" cannot be deduplicated during
4057
+ // its declaration.
4058
+ function getUnionType(types: Type[], noDeduplication?: boolean): Type {
4025
4059
if (types.length === 0) {
4026
4060
return emptyObjectType;
4027
4061
}
4028
4062
let typeSet: Type[] = [];
4029
4063
addTypesToSet(typeSet, types, TypeFlags.Union);
4030
- typeSet.sort(compareTypeIds);
4031
- if (noSubtypeReduction) {
4032
- if (containsTypeAny(typeSet)) {
4033
- return anyType;
4034
- }
4064
+ if (containsTypeAny(typeSet)) {
4065
+ return anyType;
4066
+ }
4067
+ if (noDeduplication) {
4035
4068
removeAllButLast(typeSet, undefinedType);
4036
4069
removeAllButLast(typeSet, nullType);
4037
4070
}
4038
4071
else {
4039
- removeSubtypes (typeSet);
4072
+ removeDuplicateTypes (typeSet);
4040
4073
}
4074
+ typeSet.sort(compareTypeIds);
4041
4075
if (typeSet.length === 1) {
4042
4076
return typeSet[0];
4043
4077
}
@@ -4046,19 +4080,41 @@ namespace ts {
4046
4080
if (!type) {
4047
4081
type = unionTypes[id] = <UnionType>createObjectType(TypeFlags.Union | getWideningFlagsOfTypes(typeSet));
4048
4082
type.types = typeSet;
4049
- type.reducedType = noSubtypeReduction ? undefined : type;
4050
4083
}
4051
4084
return type;
4052
4085
}
4053
4086
4054
- // Subtype reduction is basically an optimization we do to avoid excessively large union types, which take longer
4055
- // to process and look strange in quick info and error messages. Semantically there is no difference between the
4056
- // reduced type and the type itself. So, when we detect a circularity we simply say that the reduced type is the
4057
- // type itself.
4087
+ function isTypeSubtypeOfSomeType(candidate: Type, types: Type[]): boolean {
4088
+ for (let type of types) {
4089
+ if (candidate !== type && isTypeSubtypeOf(candidate, type)) {
4090
+ return true;
4091
+ }
4092
+ }
4093
+ return false;
4094
+ }
4095
+
4096
+ function removeSubtypes(types: Type[]): Type[] {
4097
+ let result = types;
4098
+ let i = result.length;
4099
+ while (i > 0) {
4100
+ i--;
4101
+ if (isTypeSubtypeOfSomeType(result[i], result)) {
4102
+ if (result === types) {
4103
+ result = types.slice(0);
4104
+ }
4105
+ result.splice(i, 1);
4106
+ }
4107
+ }
4108
+ return result;
4109
+ }
4110
+
4111
+ // The reduced type is a union type in which no constituent type is a subtype of another
4112
+ // constituent type.
4058
4113
function getReducedTypeOfUnionType(type: UnionType): Type {
4059
4114
if (!type.reducedType) {
4060
4115
type.reducedType = circularType;
4061
- let reducedType = getUnionType(type.types, /*noSubtypeReduction*/ false);
4116
+ let typesWithoutSubtypes = removeSubtypes(type.types);
4117
+ let reducedType = typesWithoutSubtypes === type.types ? type : getUnionType(typesWithoutSubtypes);
4062
4118
if (type.reducedType === circularType) {
4063
4119
type.reducedType = reducedType;
4064
4120
}
@@ -4072,7 +4128,7 @@ namespace ts {
4072
4128
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
4073
4129
let links = getNodeLinks(node);
4074
4130
if (!links.resolvedType) {
4075
- links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction */ true);
4131
+ links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noDeduplication */ true);
4076
4132
}
4077
4133
return links.resolvedType;
4078
4134
}
@@ -4355,7 +4411,7 @@ namespace ts {
4355
4411
return createTupleType(instantiateList((<TupleType>type).elementTypes, mapper, instantiateType));
4356
4412
}
4357
4413
if (type.flags & TypeFlags.Union) {
4358
- return getUnionType(instantiateList((<UnionType>type).types, mapper, instantiateType), /*noSubtypeReduction */ true);
4414
+ return getUnionType(instantiateList((<UnionType>type).types, mapper, instantiateType), /*noDeduplication */ true);
4359
4415
}
4360
4416
if (type.flags & TypeFlags.Intersection) {
4361
4417
return getIntersectionType(instantiateList((<IntersectionType>type).types, mapper, instantiateType));
0 commit comments