@@ -13113,26 +13113,28 @@ namespace ts {
13113
13113
// type arguments into sets to create a canonicalization based on those matches
13114
13114
if (relation !== identityRelation && ((source.aliasSymbol && !source.aliasTypeArgumentsContainsMarker && source.aliasTypeArguments) || (getObjectFlags(source) & ObjectFlags.Reference && (<TypeReference>source).typeArguments && !(getObjectFlags(source) & ObjectFlags.MarkerType))) &&
13115
13115
((target.aliasSymbol && !target.aliasTypeArgumentsContainsMarker && target.aliasTypeArguments) || (getObjectFlags(target) & ObjectFlags.Reference && (<TypeReference>target).typeArguments && !(getObjectFlags(target) & ObjectFlags.MarkerType)))) {
13116
- const originalKey = getRelationKey(source, target, relation);
13117
- const sourceTypeArguments = source.aliasTypeArguments || (<TypeReference>source).typeArguments!;
13118
- const targetTypeArguments = target.aliasTypeArguments || (<TypeReference>target).typeArguments!;
13119
- for (let i = 0; i < sourceTypeArguments.length; i++) {
13120
- for (let j = 0; j < targetTypeArguments.length; j++) {
13121
- if (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeIdenticalTo(sourceTypeArguments[i], targetTypeArguments[j])) {
13122
- const sourceClone = sourceTypeArguments.slice();
13123
- sourceClone[i] = markerOtherType;
13124
- const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
13125
- const targetClone = targetTypeArguments.slice();
13126
- targetClone[j] = markerOtherType;
13127
- const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
13128
- // If the marker-instantiated form looks "the same" as the type we already have (eg,
13129
- // because we replace unconstrained generics with unconstrained generics), skip the check
13130
- // since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
13131
- // entry into `recursiveTypeRelatedTo`
13132
- if (getRelationKey(s, t, relation) !== originalKey) {
13133
- const result = isRelatedTo(s, t, /*reportErrors*/ false);
13134
- if (result) {
13135
- return result;
13116
+ if (source.aliasSymbol || target.aliasSymbol || (<TypeReference>source).target !== (<TypeReference>target).target) { // ensure like symbols are just handled by standard variance analysis
13117
+ const originalKey = getRelationKey(source, target, relation);
13118
+ const sourceTypeArguments = source.aliasTypeArguments || (<TypeReference>source).typeArguments!;
13119
+ const targetTypeArguments = target.aliasTypeArguments || (<TypeReference>target).typeArguments!;
13120
+ for (let i = 0; i < sourceTypeArguments.length; i++) {
13121
+ for (let j = 0; j < targetTypeArguments.length; j++) {
13122
+ if (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeIdenticalTo(sourceTypeArguments[i], targetTypeArguments[j])) {
13123
+ const sourceClone = sourceTypeArguments.slice();
13124
+ sourceClone[i] = markerOtherType;
13125
+ const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
13126
+ const targetClone = targetTypeArguments.slice();
13127
+ targetClone[j] = markerOtherType;
13128
+ const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
13129
+ // If the marker-instantiated form looks "the same" as the type we already have (eg,
13130
+ // because we replace unconstrained generics with unconstrained generics), skip the check
13131
+ // since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
13132
+ // entry into `recursiveTypeRelatedTo`
13133
+ if (getRelationKey(s, t, relation) !== originalKey) {
13134
+ const result = isRelatedTo(s, t, /*reportErrors*/ false);
13135
+ if (result) {
13136
+ return result;
13137
+ }
13136
13138
}
13137
13139
}
13138
13140
}
@@ -14242,7 +14244,7 @@ namespace ts {
14242
14244
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
14243
14245
// expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5
14244
14246
// levels, but unequal at some level beyond that.
14245
- function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
14247
+ function isDeeplyNestedType(type: Type, stack: Type[], depth: number, maxCount = 5 ): boolean {
14246
14248
// We track all object types that have an associated symbol (representing the origin of the type)
14247
14249
if (depth >= 5 && type.flags & TypeFlags.Object) {
14248
14250
const symbol = type.symbol;
@@ -14252,7 +14254,7 @@ namespace ts {
14252
14254
const t = stack[i];
14253
14255
if (t.flags & TypeFlags.Object && t.symbol === symbol) {
14254
14256
count++;
14255
- if (count >= 5 ) return true;
14257
+ if (count >= maxCount ) return true;
14256
14258
}
14257
14259
}
14258
14260
}
@@ -15180,23 +15182,49 @@ namespace ts {
15180
15182
}
15181
15183
15182
15184
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) {
15183
- let symbolDepth: Map<number>;
15185
+ let sourceStack: Type[];
15186
+ let targetStack: Type[];
15187
+ let depth = 0;
15184
15188
let visited: Map<boolean>;
15185
15189
let bivariant = false;
15186
15190
let propagationType: Type;
15187
15191
let allowComplexConstraintInference = true;
15192
+ let expandingFlags = ExpandingFlags.None;
15188
15193
inferFromTypes(originalSource, originalTarget);
15189
15194
15190
15195
function inferFromTypes(source: Type, target: Type): void {
15191
- if (!couldContainTypeVariables(target)) {
15192
- return;
15193
- }
15194
-
15195
15196
const key = source.id + "," + target.id;
15196
15197
if (visited && visited.get(key)) {
15197
15198
return;
15198
15199
}
15199
15200
(visited || (visited = createMap<boolean>())).set(key, true);
15201
+
15202
+ const maxdepth = every(inferences, i => !!(length(i.candidates) || length(i.contraCandidates))) ? 1 : 5; // Expand up to 5 layers deep unless we've found an inference, in which case stop at 1
15203
+ const saveExpandingFlags = expandingFlags;
15204
+ if (!(expandingFlags & ExpandingFlags.Source) && isDeeplyNestedType(source, sourceStack, depth, maxdepth)) expandingFlags |= ExpandingFlags.Source;
15205
+ if (!(expandingFlags & ExpandingFlags.Target) && isDeeplyNestedType(target, targetStack, depth, maxdepth)) expandingFlags |= ExpandingFlags.Target;
15206
+ if (expandingFlags === ExpandingFlags.Both) {
15207
+ expandingFlags = saveExpandingFlags;
15208
+ return;
15209
+ }
15210
+
15211
+ if (!sourceStack) {
15212
+ sourceStack = [];
15213
+ targetStack = [];
15214
+ }
15215
+ sourceStack[depth] = source;
15216
+ targetStack[depth] = target;
15217
+ depth++;
15218
+ inferFromTypesWorker(source, target);
15219
+ depth--;
15220
+ expandingFlags = saveExpandingFlags;
15221
+ }
15222
+
15223
+ function inferFromTypesWorker(source: Type, target: Type): void {
15224
+ if (!couldContainTypeVariables(target)) {
15225
+ return;
15226
+ }
15227
+
15200
15228
if (source === wildcardType) {
15201
15229
// We are inferring from an 'any' type. We want to infer this type for every type parameter
15202
15230
// referenced in the target type, so we record it as the propagation type and infer from the
@@ -15416,26 +15444,7 @@ namespace ts {
15416
15444
source = apparentSource;
15417
15445
}
15418
15446
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
15419
- // If we are already processing another target type with the same associated symbol (such as
15420
- // an instantiation of the same generic type), we do not explore this target as it would yield
15421
- // no further inferences. We exclude the static side of classes from this check since it shares
15422
- // its symbol with the instance side which would lead to false positives.
15423
- const isNonConstructorObject = target.flags & TypeFlags.Object &&
15424
- !(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class);
15425
- const symbol = isNonConstructorObject ? target.symbol : undefined;
15426
- if (symbol) {
15427
- const id = "" + getSymbolId(symbol);
15428
- const depth = symbolDepth && symbolDepth.get(id) || 0;
15429
- if (depth > 5) {
15430
- return;
15431
- }
15432
- (symbolDepth || (symbolDepth = createMap())).set(id, depth + 1);
15433
- inferFromObjectTypes(source, target);
15434
- symbolDepth.set(id, depth);
15435
- }
15436
- else {
15437
- inferFromObjectTypes(source, target);
15438
- }
15447
+ inferFromObjectTypes(source, target);
15439
15448
}
15440
15449
}
15441
15450
}
0 commit comments