@@ -15581,29 +15581,44 @@ namespace ts {
15581
15581
// is some specialization or subtype of Q
15582
15582
// This is difficult to detect generally, so we scan for prior comparisons of the same instantiated type, and match up matching
15583
15583
// type arguments into sets to create a canonicalization based on those matches
15584
- if (relation !== identityRelation && ((source.aliasSymbol && !source.aliasTypeArgumentsContainsMarker && source.aliasTypeArguments) || (getObjectFlags(source) & ObjectFlags.Reference && !!(<TypeReference>source).node && !(getObjectFlags(source) & ObjectFlags.MarkerType))) &&
15585
- ((target.aliasSymbol && !target.aliasTypeArgumentsContainsMarker && target.aliasTypeArguments) || (getObjectFlags(target) & ObjectFlags.Reference && !!(<TypeReference>target).node && !(getObjectFlags(target) & ObjectFlags.MarkerType)))) {
15584
+ if (relation !== identityRelation && ((source.aliasSymbol && !source.aliasTypeArgumentsContainsMarker && source.aliasTypeArguments) || (getObjectFlags(source) & ObjectFlags.Reference && !!getTypeArguments (<TypeReference>source).length && !(getObjectFlags(source) & ObjectFlags.MarkerType))) &&
15585
+ ((target.aliasSymbol && !target.aliasTypeArgumentsContainsMarker && target.aliasTypeArguments) || (getObjectFlags(target) & ObjectFlags.Reference && !!getTypeArguments (<TypeReference>target).length && !(getObjectFlags(target) & ObjectFlags.MarkerType)))) {
15586
15586
if (source.aliasSymbol || target.aliasSymbol || (<TypeReference>source).target !== (<TypeReference>target).target) { // ensure like symbols are just handled by standard variance analysis
15587
- const originalKey = getRelationKey(source, target, intersectionState, relation);
15588
15587
const sourceTypeArguments = source.aliasTypeArguments || getTypeArguments(<TypeReference>source);
15588
+ const sourceHasMarker = some(sourceTypeArguments, a => a === markerOtherType);
15589
15589
const targetTypeArguments = target.aliasTypeArguments || getTypeArguments(<TypeReference>target);
15590
- for (let i = 0; i < sourceTypeArguments.length; i++) {
15591
- for (let j = 0; j < targetTypeArguments.length; j++) {
15592
- if (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeIdenticalTo(sourceTypeArguments[i], targetTypeArguments[j])) {
15593
- const sourceClone = sourceTypeArguments.slice();
15594
- sourceClone[i] = markerOtherType;
15595
- const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
15596
- const targetClone = targetTypeArguments.slice();
15597
- targetClone[j] = markerOtherType;
15598
- const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
15599
- // If the marker-instantiated form looks "the same" as the type we already have (eg,
15600
- // because we replace unconstrained generics with unconstrained generics), skip the check
15601
- // since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
15602
- // entry into `recursiveTypeRelatedTo`
15603
- if (getRelationKey(s, t, intersectionState, relation) !== originalKey) {
15604
- const result = isRelatedTo(s, t, /*reportErrors*/ false);
15605
- if (result) {
15606
- return result;
15590
+ const targetHasMarker = some(targetTypeArguments, a => a === markerOtherType);
15591
+ // We're using `markerOtherType` as an existential, so we can't use it again if it's already in use,
15592
+ // as we'd get spurious equivalencies - we'd need to use a second existential type, and once we're doing
15593
+ // that we lose a lot of the benefit of canonicalizing back to a single-existential comparison, since then
15594
+ // we'd need to manufacture new type identities for every new existential we make
15595
+ // The above checks don't catch all cases this can occur, as they can only detect when the containing type
15596
+ // was flagged during construction as containing a marker; however if a marker enters a type through instantiation
15597
+ // we need to catch that here.
15598
+ if (!sourceHasMarker && !targetHasMarker) {
15599
+ const originalKey = getRelationKey(source, target, intersectionState, relation);
15600
+ for (let i = 0; i < sourceTypeArguments.length; i++) {
15601
+ for (let j = 0; j < targetTypeArguments.length; j++) {
15602
+ if ((!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && !isTypeAny(sourceTypeArguments[i]) && isTypeIdenticalTo(sourceTypeArguments[i], targetTypeArguments[j])) ||
15603
+ // Similarly, if we're comparing X<Q> to Z<any>, X<Q> is assignable to Z<any> trivially if X<?> is assignable to Z<?>
15604
+ (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeAny(targetTypeArguments[j])) ||
15605
+ // Again, but for `X<any>` vs `Z<Q>`
15606
+ (isTypeAny(sourceTypeArguments[i]) && !(targetTypeArguments[j].flags & TypeFlags.TypeParameter))) {
15607
+ const sourceClone = sourceTypeArguments.slice();
15608
+ sourceClone[i] = markerOtherType;
15609
+ const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
15610
+ const targetClone = targetTypeArguments.slice();
15611
+ targetClone[j] = markerOtherType;
15612
+ const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
15613
+ // If the marker-instantiated form looks "the same" as the type we already have (eg,
15614
+ // because we replace unconstrained generics with unconstrained generics), skip the check
15615
+ // since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
15616
+ // entry into `recursiveTypeRelatedTo`
15617
+ if (getRelationKey(s, t, intersectionState, relation) !== originalKey) {
15618
+ const result = isRelatedTo(s, t, /*reportErrors*/ false);
15619
+ if (result) {
15620
+ return result;
15621
+ }
15607
15622
}
15608
15623
}
15609
15624
}
0 commit comments