@@ -3119,7 +3119,7 @@ namespace ts {
3119
3119
}
3120
3120
3121
3121
function resolveTupleTypeMembers(type: TupleType) {
3122
- let arrayType = resolveStructuredTypeMembers(createArrayType(getUnionType(type.elementTypes, /*noDeduplication */ true)));
3122
+ let arrayType = resolveStructuredTypeMembers(createArrayType(getUnionType(type.elementTypes, /*noSubtypeReduction */ true)));
3123
3123
let members = createTupleTypeMemberSymbols(type.elementTypes);
3124
3124
addInheritedMembers(members, arrayType.properties);
3125
3125
setObjectTypeMembers(type, members, arrayType.callSignatures, arrayType.constructSignatures, arrayType.stringIndexType, arrayType.numberIndexType);
@@ -3451,29 +3451,6 @@ namespace ts {
3451
3451
return undefined;
3452
3452
}
3453
3453
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
-
3477
3454
function getSignaturesOfStructuredType(type: Type, kind: SignatureKind): Signature[] {
3478
3455
if (type.flags & TypeFlags.StructuredType) {
3479
3456
let resolved = resolveStructuredTypeMembers(<ObjectType>type);
@@ -4103,73 +4080,20 @@ namespace ts {
4103
4080
}
4104
4081
}
4105
4082
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 (let i = 0, len = types.length; i < len; i++) {
4085
+ if (candidate !== types[i] && isTypeSubtypeOf(candidate, types[i])) {
4162
4086
return true;
4163
4087
}
4164
4088
}
4165
4089
return false;
4166
4090
}
4167
4091
4168
- function removeDuplicateTypes (types: Type[]) {
4092
+ function removeSubtypes (types: Type[]) {
4169
4093
let i = types.length;
4170
4094
while (i > 0) {
4171
4095
i--;
4172
- if (isTypeDuplicateOfSomeType (types[i], types)) {
4096
+ if (isSubtypeOfAny (types[i], types)) {
4173
4097
types.splice(i, 1);
4174
4098
}
4175
4099
}
@@ -4194,12 +4118,14 @@ namespace ts {
4194
4118
}
4195
4119
}
4196
4120
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 {
4203
4129
if (types.length === 0) {
4204
4130
return emptyObjectType;
4205
4131
}
@@ -4208,12 +4134,12 @@ namespace ts {
4208
4134
if (containsTypeAny(typeSet)) {
4209
4135
return anyType;
4210
4136
}
4211
- if (noDeduplication ) {
4137
+ if (noSubtypeReduction ) {
4212
4138
removeAllButLast(typeSet, undefinedType);
4213
4139
removeAllButLast(typeSet, nullType);
4214
4140
}
4215
4141
else {
4216
- removeDuplicateTypes (typeSet);
4142
+ removeSubtypes (typeSet);
4217
4143
}
4218
4144
if (typeSet.length === 1) {
4219
4145
return typeSet[0];
@@ -4230,7 +4156,7 @@ namespace ts {
4230
4156
function getTypeFromUnionTypeNode(node: UnionTypeNode): Type {
4231
4157
let links = getNodeLinks(node);
4232
4158
if (!links.resolvedType) {
4233
- links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noDeduplication */ true);
4159
+ links.resolvedType = getUnionType(map(node.types, getTypeFromTypeNode), /*noSubtypeReduction */ true);
4234
4160
}
4235
4161
return links.resolvedType;
4236
4162
}
@@ -4526,7 +4452,7 @@ namespace ts {
4526
4452
return createTupleType(instantiateList((<TupleType>type).elementTypes, mapper, instantiateType));
4527
4453
}
4528
4454
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);
4530
4456
}
4531
4457
if (type.flags & TypeFlags.Intersection) {
4532
4458
return getIntersectionType(instantiateList((<IntersectionType>type).types, mapper, instantiateType));
@@ -4813,6 +4739,30 @@ namespace ts {
4813
4739
return Ternary.False;
4814
4740
}
4815
4741
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
+
4816
4766
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
4817
4767
for (let prop of getPropertiesOfObjectType(source)) {
4818
4768
if (!isKnownProperty(target, prop.name)) {
@@ -5594,7 +5544,7 @@ namespace ts {
5594
5544
return getWidenedTypeOfObjectLiteral(type);
5595
5545
}
5596
5546
if (type.flags & TypeFlags.Union) {
5597
- return getUnionType(map((<UnionType>type).types, getWidenedType));
5547
+ return getUnionType(map((<UnionType>type).types, getWidenedType), /*noSubtypeReduction*/ true );
5598
5548
}
5599
5549
if (isArrayType(type)) {
5600
5550
return createArrayType(getWidenedType((<TypeReference>type).typeArguments[0]));
0 commit comments