@@ -17699,9 +17699,16 @@ namespace ts {
17699
17699
targetStack = [];
17700
17700
}
17701
17701
else {
17702
+ // generate a key where all type parameter id positions are replaced with unconstrained type parameter ids
17703
+ // this isn't perfect - nested type references passed as type arguments will muck up the indexes and thus
17704
+ // prevent finding matches- but it should hit up the common cases
17705
+ const broadestEquivalentId = id.split(",").map(i => i.replace(/-\d+/g, (_match, offset: number) => {
17706
+ const index = length(id.slice(0, offset).match(/[-=]/g) || undefined);
17707
+ return `=${index}`;
17708
+ })).join(",");
17702
17709
for (let i = 0; i < maybeCount; i++) {
17703
17710
// If source and target are already being compared, consider them related with assumptions
17704
- if (id === maybeKeys[i]) {
17711
+ if (id === maybeKeys[i] || broadestEquivalentId === maybeKeys[i] ) {
17705
17712
return Ternary.Maybe;
17706
17713
}
17707
17714
}
@@ -17773,13 +17780,6 @@ namespace ts {
17773
17780
return result;
17774
17781
}
17775
17782
17776
- function getInstanceOfAliasOrReferenceWithMarker(input: Type, typeArguments: readonly Type[]) {
17777
- const s = input.aliasSymbol ? getTypeAliasInstantiation(input.aliasSymbol, typeArguments) : createTypeReference((<TypeReference>input).target, typeArguments);
17778
- if (s.aliasSymbol) s.aliasTypeArgumentsContainsMarker = true;
17779
- else (<TypeReference>s).objectFlags |= ObjectFlags.MarkerType;
17780
- return s;
17781
- }
17782
-
17783
17783
function structuredTypeRelatedToWorker(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
17784
17784
if (intersectionState & IntersectionState.PropertyCheck) {
17785
17785
return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
@@ -17868,67 +17868,6 @@ namespace ts {
17868
17868
}
17869
17869
}
17870
17870
17871
- // If a more _general_ version of the source and target are being compared, consider them related with assumptions
17872
- // eg, if { x: Q } and { x: Q, y: A } are being compared and we're about to look at { x: Q' } and { x: Q', y: A } where Q'
17873
- // is some specialization or subtype of Q
17874
- // This is difficult to detect generally, so we scan for prior comparisons of the same instantiated type, and match up matching
17875
- // type arguments into sets to create a canonicalization based on those matches
17876
- if (relation !== identityRelation && ((source.aliasSymbol && !source.aliasTypeArgumentsContainsMarker && source.aliasTypeArguments) || (getObjectFlags(source) & ObjectFlags.Reference && !!getTypeArguments(<TypeReference>source).length && !(getObjectFlags(source) & ObjectFlags.MarkerType))) &&
17877
- ((target.aliasSymbol && !target.aliasTypeArgumentsContainsMarker && target.aliasTypeArguments) || (getObjectFlags(target) & ObjectFlags.Reference && !!getTypeArguments(<TypeReference>target).length && !(getObjectFlags(target) & ObjectFlags.MarkerType)))) {
17878
- if (source.aliasSymbol || target.aliasSymbol || (<TypeReference>source).target !== (<TypeReference>target).target) { // ensure like symbols are just handled by standard variance analysis
17879
- const sourceTypeArguments = source.aliasTypeArguments || getTypeArguments(<TypeReference>source);
17880
- const sourceHasMarker = some(sourceTypeArguments, a => a === markerOtherType);
17881
- const targetTypeArguments = target.aliasTypeArguments || getTypeArguments(<TypeReference>target);
17882
- const targetHasMarker = some(targetTypeArguments, a => a === markerOtherType);
17883
- // We're using `markerOtherType` as an existential, so we can't use it again if it's already in use,
17884
- // as we'd get spurious equivalencies - we'd need to use a second existential type, and once we're doing
17885
- // that we lose a lot of the benefit of canonicalizing back to a single-existential comparison, since then
17886
- // we'd need to manufacture new type identities for every new existential we make
17887
- // The above checks don't catch all cases this can occur, as they can only detect when the containing type
17888
- // was flagged during construction as containing a marker; however if a marker enters a type through instantiation
17889
- // we need to catch that here.
17890
- // We only do this when there's a handful of possible ways to match the type parameters up, as otherwise we manufacture
17891
- // an inordinate quantity of types just to calculate their IDs!
17892
- if (!sourceHasMarker && !targetHasMarker && sourceTypeArguments.length * targetTypeArguments.length < 10) {
17893
- const originalKey = getRelationKey(source, target, intersectionState, relation);
17894
- for (let i = 0; i < sourceTypeArguments.length; i++) {
17895
- for (let j = 0; j < targetTypeArguments.length; j++) {
17896
- if ((!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && !isTypeAny(sourceTypeArguments[i]) && sourceTypeArguments[i] === targetTypeArguments[j]) ||
17897
- // Similarly, if we're comparing X<Q> to Z<any>, X<Q> is assignable to Z<any> trivially if X<?> is assignable to Z<?>
17898
- (!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && isTypeAny(targetTypeArguments[j])) ||
17899
- // Again, but for `X<any>` vs `Z<Q>`
17900
- (isTypeAny(sourceTypeArguments[i]) && !(targetTypeArguments[j].flags & TypeFlags.TypeParameter)) ||
17901
- // Likewise, if we're comparing X<U> to Z<U> and are already comparing X<T> to Z<T>, we can assume it to be true
17902
- !!(sourceTypeArguments[i].flags & TypeFlags.TypeParameter) && sourceTypeArguments[i] === targetTypeArguments[j]) {
17903
- const sourceClone = sourceTypeArguments.slice();
17904
- sourceClone[i] = markerOtherType;
17905
- const s = getInstanceOfAliasOrReferenceWithMarker(source, sourceClone);
17906
- const targetClone = targetTypeArguments.slice();
17907
- targetClone[j] = markerOtherType;
17908
- const t = getInstanceOfAliasOrReferenceWithMarker(target, targetClone);
17909
- // If the marker-instantiated form looks "the same" as the type we already have (eg,
17910
- // because we replace unconstrained generics with unconstrained generics), skip the check
17911
- // since we'll otherwise deliver a spurious `Maybe` result from the key _just_ set upon
17912
- // entry into `recursiveTypeRelatedTo`
17913
- const existentialKey = getRelationKey(s, t, intersectionState, relation);
17914
- if (existentialKey !== originalKey) {
17915
- // We don't actually trigger the comparison, since we'd rather not do an extra comparison
17916
- // if we haven't already started that more general comparison; instead we just look for the
17917
- // key in the maybeKeys stack
17918
- for (let i = 0; i < maybeCount; i++) {
17919
- // If source and target are already being compared, consider them related with assumptions
17920
- if (existentialKey === maybeKeys[i]) {
17921
- return Ternary.Maybe;
17922
- }
17923
- }
17924
- }
17925
- }
17926
- }
17927
- }
17928
- }
17929
- }
17930
- }
17931
-
17932
17871
// For a generic type T and a type U that is assignable to T, [...U] is assignable to T, U is assignable to readonly [...T],
17933
17872
// and U is assignable to [...T] when U is constrained to a mutable array or tuple type.
17934
17873
if (isSingleElementGenericTupleType(source) && !source.target.readonly && (result = isRelatedTo(getTypeArguments(source)[0], target)) ||
@@ -19209,7 +19148,7 @@ namespace ts {
19209
19148
}
19210
19149
19211
19150
function isTypeReferenceWithGenericArguments(type: Type): boolean {
19212
- return isNonDeferredTypeReference(type) && some(getTypeArguments(type), t => isUnconstrainedTypeParameter(t ) || isTypeReferenceWithGenericArguments(t));
19151
+ return isNonDeferredTypeReference(type) && some(getTypeArguments(type), t => !!(t.flags & TypeFlags.TypeParameter ) || isTypeReferenceWithGenericArguments(t));
19213
19152
}
19214
19153
19215
19154
/**
0 commit comments