@@ -16821,7 +16821,7 @@ namespace ts {
16821
16821
if (source.flags & (TypeFlags.Object | TypeFlags.Conditional) && source.aliasSymbol &&
16822
16822
source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol &&
16823
16823
!(source.aliasTypeArgumentsContainsMarker || target.aliasTypeArgumentsContainsMarker)) {
16824
- const variances = getAliasVariances(source.aliasSymbol);
16824
+ const variances = getAliasVariances(source.aliasSymbol, relation === assignableRelation ? isRelatedTo : compareTypesAssignable );
16825
16825
if (variances === emptyArray) {
16826
16826
return Ternary.Maybe;
16827
16827
}
@@ -17031,7 +17031,7 @@ namespace ts {
17031
17031
// We have type references to the same generic type, and the type references are not marker
17032
17032
// type references (which are intended by be compared structurally). Obtain the variance
17033
17033
// information for the type parameters and relate the type arguments accordingly.
17034
- const variances = getVariances((<TypeReference>source).target);
17034
+ const variances = getVariances((<TypeReference>source).target, relation === assignableRelation ? isRelatedTo : compareTypesAssignable );
17035
17035
// We return Ternary.Maybe for a recursive invocation of getVariances (signalled by emptyArray). This
17036
17036
// effectively means we measure variance only from type parameter occurrences that aren't nested in
17037
17037
// recursive instantiations of the generic type.
@@ -17903,21 +17903,21 @@ namespace ts {
17903
17903
return result;
17904
17904
}
17905
17905
17906
- function getAliasVariances(symbol: Symbol) {
17906
+ function getAliasVariances(symbol: Symbol, compareTypes: (source: Type, target: Type) => Ternary ) {
17907
17907
const links = getSymbolLinks(symbol);
17908
17908
return getVariancesWorker(links.typeParameters, links, (_links, param, marker) => {
17909
17909
const type = getTypeAliasInstantiation(symbol, instantiateTypes(links.typeParameters!, makeUnaryTypeMapper(param, marker)));
17910
17910
type.aliasTypeArgumentsContainsMarker = true;
17911
17911
return type;
17912
- });
17912
+ }, compareTypes );
17913
17913
}
17914
17914
17915
17915
// Return an array containing the variance of each type parameter. The variance is effectively
17916
17916
// a digest of the type comparisons that occur for each type argument when instantiations of the
17917
17917
// generic type are structurally compared. We infer the variance information by comparing
17918
17918
// instantiations of the generic type for type arguments with known relations. The function
17919
17919
// returns the emptyArray singleton when invoked recursively for the given generic type.
17920
- function getVariancesWorker<TCache extends { variances?: VarianceFlags[] }>(typeParameters: readonly TypeParameter[] = emptyArray, cache: TCache, createMarkerType: (input: TCache, param: TypeParameter, marker: Type) => Type): VarianceFlags[] {
17920
+ 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[] {
17921
17921
let variances = cache.variances;
17922
17922
if (!variances) {
17923
17923
// The emptyArray singleton is used to signal a recursive invocation.
@@ -17933,13 +17933,20 @@ namespace ts {
17933
17933
// invariance, covariance, contravariance or bivariance.
17934
17934
const typeWithSuper = createMarkerType(cache, tp, markerSuperType);
17935
17935
const typeWithSub = createMarkerType(cache, tp, markerSubType);
17936
- let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) |
17937
- (isTypeAssignableTo(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0);
17936
+ // Note: We consider a `Maybe` result to be affirmative below; if we're already trying to figure out if A<+?> is assignable to A<-?>
17937
+ // 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
17938
+ // for the scope of the remainder of the comparison. (Simply because disproving it is the point of the _other_ comparisons we're performing)
17939
+ // The issue comes with invalidating the cached variance result if the root comparison turns out to be negative. _Right now_, we're simply...
17940
+ // not. This will _probably_ cause some subtle bugs, however coming up with an example exposing one such bug is nontrivial.
17941
+ // But! In the event such a motiviating example should come to light, the fix shouldn't be _too_ bad - here in `getVariancesWorker` we'd
17942
+ // just need a `Maybe` tracking stack just like we have in `recursiveTypeRelatedTo`.
17943
+ let variance = (compareTypes(typeWithSub, typeWithSuper) ? VarianceFlags.Covariant : 0) |
17944
+ (compareTypes(typeWithSuper, typeWithSub) ? VarianceFlags.Contravariant : 0);
17938
17945
// If the instantiations appear to be related bivariantly it may be because the
17939
17946
// type parameter is independent (i.e. it isn't witnessed anywhere in the generic
17940
17947
// type). To determine this we compare instantiations where the type parameter is
17941
17948
// replaced with marker types that are known to be unrelated.
17942
- if (variance === VarianceFlags.Bivariant && isTypeAssignableTo (createMarkerType(cache, tp, markerOtherType), typeWithSuper)) {
17949
+ if (variance === VarianceFlags.Bivariant && compareTypes (createMarkerType(cache, tp, markerOtherType), typeWithSuper)) {
17943
17950
variance = VarianceFlags.Independent;
17944
17951
}
17945
17952
outofbandVarianceMarkerHandler = oldHandler;
@@ -17958,12 +17965,12 @@ namespace ts {
17958
17965
return variances;
17959
17966
}
17960
17967
17961
- function getVariances(type: GenericType): VarianceFlags[] {
17968
+ function getVariances(type: GenericType, compareTypes: (source: Type, target: Type) => Ternary ): VarianceFlags[] {
17962
17969
// Arrays and tuples are known to be covariant, no need to spend time computing this.
17963
17970
if (type === globalArrayType || type === globalReadonlyArrayType || type.objectFlags & ObjectFlags.Tuple) {
17964
17971
return arrayVariances;
17965
17972
}
17966
- return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference);
17973
+ return getVariancesWorker(type.typeParameters, type, getMarkerTypeReference, compareTypes );
17967
17974
}
17968
17975
17969
17976
// Return true if the given type reference has a 'void' type argument for a covariant type parameter.
@@ -19209,7 +19216,7 @@ namespace ts {
19209
19216
if (source.aliasSymbol && source.aliasTypeArguments && source.aliasSymbol === target.aliasSymbol) {
19210
19217
// Source and target are types originating in the same generic type alias declaration.
19211
19218
// Simply infer from source type arguments to target type arguments.
19212
- inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol));
19219
+ inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol, compareTypesAssignable ));
19213
19220
return;
19214
19221
}
19215
19222
if (source === target && source.flags & TypeFlags.UnionOrIntersection) {
@@ -19331,7 +19338,7 @@ namespace ts {
19331
19338
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target)) &&
19332
19339
!((<TypeReference>source).node && (<TypeReference>target).node)) {
19333
19340
// If source and target are references to the same generic type, infer from type arguments
19334
- inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target));
19341
+ inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target, compareTypesAssignable ));
19335
19342
}
19336
19343
else if (source.flags & TypeFlags.Index && target.flags & TypeFlags.Index) {
19337
19344
contravariant = !contravariant;
@@ -19642,7 +19649,7 @@ namespace ts {
19642
19649
if (getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
19643
19650
(<TypeReference>source).target === (<TypeReference>target).target || isArrayType(source) && isArrayType(target))) {
19644
19651
// If source and target are references to the same generic type, infer from type arguments
19645
- inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target));
19652
+ inferFromTypeArguments(getTypeArguments(<TypeReference>source), getTypeArguments(<TypeReference>target), getVariances((<TypeReference>source).target, compareTypesAssignable ));
19646
19653
return;
19647
19654
}
19648
19655
if (isGenericMappedType(source) && isGenericMappedType(target)) {
0 commit comments