@@ -16864,7 +16864,7 @@ namespace ts {
16864
16864
if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol &&
16865
16865
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol &&
16866
16866
!(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) {
16867
- const variances = getAliasVariances(source.aliasSymbol);
16867
+ const variances = getAliasVariances(source.aliasSymbol, relation === assignableRelation ? isRelatedTo : compareTypesAssignable );
16868
16868
if (variances === emptyArray) {
16869
16869
return Ternary.Maybe;
16870
16870
}
@@ -17095,7 +17095,7 @@ namespace ts {
17095
17095
// We have type references to the same generic type, and the type references are not marker
17096
17096
// type references (which are intended by be compared structurally). Obtain the variance
17097
17097
// information for the type parameters and relate the type arguments accordingly.
17098
- const variances = getVariances((<TypeReference>source).target);
17098
+ const variances = getVariances((<TypeReference>source).target, relation === assignableRelation ? isRelatedTo : compareTypesAssignable );
17099
17099
// We return Ternary.Maybe for a recursive invocation of getVariances (signalled by emptyArray). This
17100
17100
// effectively means we measure variance only from type parameter occurrences that aren't nested in
17101
17101
// recursive instantiations of the generic type.
@@ -17988,21 +17988,21 @@ namespace ts {
17988
17988
return result;
17989
17989
}
17990
17990
17991
- function getAliasVariances(symbol: Symbol) {
17991
+ function getAliasVariances(symbol: Symbol, compareTypes: (source: Type, target: Type) => Ternary ) {
17992
17992
const links = getSymbolLinks(symbol);
17993
17993
return getVariancesWorker(links.typeParameters, links, (_links, param, marker) => {
17994
17994
const type = getTypeAliasInstantiation(symbol, instantiateTypes(links.typeParameters!, makeUnaryTypeMapper(param, marker)));
17995
17995
type.aliasTypeArgumentsContainsMarker = true;
17996
17996
return type;
17997
- });
17997
+ }, compareTypes );
17998
17998
}
17999
17999
18000
18000
// Return an array containing the variance of each type parameter. The variance is effectively
18001
18001
// a digest of the type comparisons that occur for each type argument when instantiations of the
18002
18002
// generic type are structurally compared. We infer the variance information by comparing
18003
18003
// instantiations of the generic type for type arguments with known relations. The function
18004
18004
// returns the emptyArray singleton when invoked recursively for the given generic type.
18005
- function getVariancesWorker<TCache extends { variances?: VarianceFlags[] }>(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] {
18005
+ function getVariancesWorker<TCache extends { variances?: VarianceFlags[] }>(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type, compareTypes: (source: Type, target: Type) => Ternary ): VarianceFlags[] {
18006
18006
let variances = cache.variances;
18007
18007
if (!variances) {
18008
18008
// The emptyArray singleton is used to signal a recursive invocation.
@@ -18018,13 +18018,20 @@ namespace ts {
18018
18018
// invariance, covariance, contravariance or bivariance.
18019
18019
const typeWithSuper = createMarkerType(cache, tp, markerSuperType);
18020
18020
const typeWithSub = createMarkerType(cache, tp, markerSubType);
18021
- let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) |
18022
- (isTypeAssignableTo(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0);
18021
+ // Note: We consider a `Maybe` result to be affirmative below; if we're already trying to figure out if A<+?> is assignable to A<-?>
18022
+ // and we end up needing to check some type we're already comparing in the body of the calling comparison, we can assume it to be true
18023
+ // for the scope of the remainder of the comparison. (Simply because disproving it is the point of the _other_ comparisons we're performing)
18024
+ // The issue comes with invalidating the cached variance result if the root comparison turns out to be negative. _Right now_, we're simply...
18025
+ // not. This will _probably_ cause some subtle bugs, however coming up with an example exposing one such bug is nontrivial.
18026
+ // But! In the event such a motiviating example should come to light, the fix shouldn't be _too_ bad - here in `getVariancesWorker` we'd
18027
+ // just need a `Maybe` tracking stack just like we have in `recursiveTypeRelatedTo`.
18028
+ let variance = (compareTypes(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) |
18029
+ (compareTypes(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0);
18023
18030
// If the instantiations appear to be related bivariantly it may be because the
18024
18031
// type parameter is independent (i.e. it isn't witnessed anywhere in the generic
18025
18032
// type). To determine this we compare instantiations where the type parameter is
18026
18033
// replaced with marker types that are known to be unrelated.
18027
- if (variance === VarianceFlags.Bivariant && isTypeAssignableTo (createMarkerType(cache, tp, markerOtherType), typeWithSuper)) {
18034
+ if (variance === VarianceFlags.Bivariant && compareTypes (createMarkerType(cache, tp, markerOtherType), typeWithSuper)) {
18028
18035
variance = VarianceFlags.Independent;
18029
18036
}
18030
18037
outofbandVarianceMarkerHandler = oldHandler;
@@ -18043,12 +18050,12 @@ namespace ts {
18043
18050
return variances;
18044
18051
}
18045
18052
18046
- function getVariances(type: GenericType): VarianceFlags[] {
18053
+ function getVariances(type: GenericType, compareTypes: (source: Type, target: Type) => Ternary ): VarianceFlags[] {
18047
18054
// Arrays and tuples are known to be covariant, no need to spend time computing this.
18048
18055
if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
18049
18056
return arrayVariances;
18050
18057
}
18051
- return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference);
18058
+ return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference, compareTypes );
18052
18059
}
18053
18060
18054
18061
// Return true if the given type reference has a 'void' type argument for a covariant type parameter.
@@ -19313,7 +19320,7 @@ namespace ts {
19313
19320
if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
19314
19321
// Source and target are types originating in the same generic type alias declaration.
19315
19322
// Simply infer from source type arguments to target type arguments.
19316
- inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol));
19323
+ inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol, compareTypesAssignable ));
19317
19324
return;
19318
19325
}
19319
19326
if (source === target && source.flags & TypeFlags.UnionOrIntersection) {
@@ -19435,7 +19442,7 @@ namespace ts {
19435
19442
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target)) &&
19436
19443
!((<TypeReference>source).node && (<TypeReference>target).node)) {
19437
19444
// If source and target are references to the same generic type, infer from type arguments
19438
- inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target));
19445
+ inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target, compareTypesAssignable ));
19439
19446
}
19440
19447
else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) {
19441
19448
contravariant = !contravariant;
@@ -19758,7 +19765,7 @@ namespace ts {
19758
19765
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
19759
19766
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target))) {
19760
19767
// If source and target are references to the same generic type, infer from type arguments
19761
- inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target));
19768
+ inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target, compareTypesAssignable ));
19762
19769
return;
19763
19770
}
19764
19771
if (isGenericMappedType(source) && isGenericMappedType(target)) {
0 commit comments