@@ -285,6 +285,7 @@ namespace ts {
285
285
const markerSuperType = <TypeParameter>createType(TypeFlags.TypeParameter);
286
286
const markerSubType = <TypeParameter>createType(TypeFlags.TypeParameter);
287
287
markerSubType.constraint = markerSuperType;
288
+ const markerOtherType = <TypeParameter>createType(TypeFlags.TypeParameter);
288
289
289
290
const anySignature = createSignature(undefined, undefined, undefined, emptyArray, anyType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
290
291
const unknownSignature = createSignature(undefined, undefined, undefined, emptyArray, unknownType, /*typePredicate*/ undefined, 0, /*hasRestParameter*/ false, /*hasLiteralTypes*/ false);
@@ -3548,7 +3549,7 @@ namespace ts {
3548
3549
return;
3549
3550
}
3550
3551
3551
- if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length) {
3552
+ if (resolved.callSignatures.length === 1 && !resolved.constructSignatures.length && isStrictSignature(resolved.callSignatures[0]) ) {
3552
3553
const parenthesizeSignature = shouldAddParenthesisAroundFunctionType(resolved.callSignatures[0], flags);
3553
3554
if (parenthesizeSignature) {
3554
3555
writePunctuation(writer, SyntaxKind.OpenParenToken);
@@ -3559,7 +3560,7 @@ namespace ts {
3559
3560
}
3560
3561
return;
3561
3562
}
3562
- if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length) {
3563
+ if (resolved.constructSignatures.length === 1 && !resolved.callSignatures.length && isStrictSignature(resolved.constructSignatures[0]) ) {
3563
3564
if (flags & TypeFormatFlags.InElementType) {
3564
3565
writePunctuation(writer, SyntaxKind.OpenParenToken);
3565
3566
}
@@ -8521,6 +8522,17 @@ namespace ts {
8521
8522
/*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
8522
8523
}
8523
8524
8525
+ // A signature is considered strict if it is declared in a function type literal, a constructor type
8526
+ // literal, a function expression, an arrow function, or a function declaration with no overloads. A
8527
+ // strict signature is subject to strict checking in strictFunctionTypes mode.
8528
+ function isStrictSignature(signature: Signature) {
8529
+ const declaration = signature.declaration;
8530
+ const kind = declaration ? declaration.kind : SyntaxKind.Unknown;
8531
+ return kind === SyntaxKind.FunctionType || kind === SyntaxKind.ConstructorType ||
8532
+ kind === SyntaxKind.FunctionExpression || kind === SyntaxKind.ArrowFunction ||
8533
+ (kind === SyntaxKind.FunctionDeclaration && getSingleCallSignature(getTypeOfSymbol(getSymbolOfNode(declaration))));
8534
+ }
8535
+
8524
8536
type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
8525
8537
8526
8538
/**
@@ -8546,9 +8558,7 @@ namespace ts {
8546
8558
source = instantiateSignatureInContextOf(source, target, /*contextualMapper*/ undefined, compareTypes);
8547
8559
}
8548
8560
8549
- const targetKind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
8550
- const strictVariance = strictFunctionTypes && targetKind !== SyntaxKind.MethodDeclaration && targetKind !== SyntaxKind.MethodSignature;
8551
-
8561
+ const strictVariance = strictFunctionTypes && isStrictSignature(target);
8552
8562
let result = Ternary.True;
8553
8563
8554
8564
const sourceThisType = getThisTypeOfSignature(source);
@@ -9215,15 +9225,41 @@ namespace ts {
9215
9225
// in the process of computing variance information for recursive types and when
9216
9226
// comparing 'this' type arguments.
9217
9227
const variance = i < variances.length ? variances[i] : Variance.Covariant;
9218
- const s = sources[i];
9219
- const t = targets[i];
9220
- const related = variance === Variance.Covariant ? isRelatedTo(s, t, reportErrors) :
9221
- variance === Variance.Contravariant ? isRelatedTo(t, s, reportErrors) :
9222
- Ternary.False;
9223
- if (!related) {
9224
- return Ternary.False;
9228
+ // We ignore arguments for independent type parameters (because they're never witnessed).
9229
+ if (variance !== Variance.Independent) {
9230
+ const s = sources[i];
9231
+ const t = targets[i];
9232
+ let related = Ternary.True;
9233
+ if (variance === Variance.Covariant) {
9234
+ related = isRelatedTo(s, t, reportErrors);
9235
+ }
9236
+ else if (variance === Variance.Contravariant) {
9237
+ related = isRelatedTo(t, s, reportErrors);
9238
+ }
9239
+ else if (variance === Variance.Bivariant) {
9240
+ // In the bivariant case we first compare contravariantly without reporting
9241
+ // errors. Then, if that doesn't succeed, we compare covariantly with error
9242
+ // reporting. Thus, error elaboration will be based on the the covariant check,
9243
+ // which is generally easier to reason about.
9244
+ related = isRelatedTo(t, s, /*reportErrors*/ false);
9245
+ if (!related) {
9246
+ related = isRelatedTo(s, t, reportErrors);
9247
+ }
9248
+ }
9249
+ else {
9250
+ // In the invariant case we first compare covariantly, and only when that
9251
+ // succeeds do we proceed to compare contravariantly. Thus, error elaboration
9252
+ // will typically be based on the covariant check.
9253
+ related = isRelatedTo(s, t, reportErrors);
9254
+ if (related) {
9255
+ related &= isRelatedTo(t, s, reportErrors);
9256
+ }
9257
+ }
9258
+ if (!related) {
9259
+ return Ternary.False;
9260
+ }
9261
+ result &= related;
9225
9262
}
9226
- result &= related;
9227
9263
}
9228
9264
return result;
9229
9265
}
@@ -9388,14 +9424,21 @@ namespace ts {
9388
9424
!(source.flags & TypeFlags.MarkerType || target.flags & TypeFlags.MarkerType)) {
9389
9425
// We have type references to the same generic type, and the type references are not marker
9390
9426
// type references (which are intended by be compared structurally). Obtain the variance
9391
- // information for the type parameters and relate the type arguments accordingly. If we do
9392
- // not succeed, fall through and do a structural comparison instead (there are instances
9393
- // where the variance information isn't accurate, e.g. when type parameters are used only
9394
- // in bivariant positions or when a type argument is 'any' or 'void'.)
9427
+ // information for the type parameters and relate the type arguments accordingly.
9395
9428
const variances = getVariances((<TypeReference>source).target);
9396
9429
if (result = typeArgumentsRelatedTo(<TypeReference>source, <TypeReference>target, variances, reportErrors)) {
9397
9430
return result;
9398
9431
}
9432
+ // The type arguments did not relate appropriately, but it may be because we have no variance
9433
+ // information (in which case typeArgumentsRelatedTo defaulted to covariance for all type
9434
+ // arguments). It might also be the case that the target type has a 'void' type argument for
9435
+ // a covariant type parameter that is only used in return positions within the generic type
9436
+ // (in which case any type argument is permitted on the source side). In those cases we proceed
9437
+ // with a structural comparison. Otherwise, we know for certain the instantiations aren't
9438
+ // related and we can return here.
9439
+ if (variances !== emptyArray && !hasCovariantVoidArgument(<TypeReference>target, variances)) {
9440
+ return Ternary.False;
9441
+ }
9399
9442
}
9400
9443
// Even if relationship doesn't hold for unions, intersections, or generic type references,
9401
9444
// it may hold in a structural comparison.
@@ -9814,14 +9857,12 @@ namespace ts {
9814
9857
return result;
9815
9858
}
9816
9859
9817
- // Return an array containing the variance of each type parameter. The variance information is
9818
- // computed by comparing instantiations of the generic type for type arguments with known relations.
9819
- // A type parameter is marked as covariant if a covariant comparison succeeds; otherwise, it is
9820
- // marked contravariant if a contravarint comparison succeeds; otherwise, it is marked invariant.
9821
- // One form of variance doesn't exclude another, so this information simply serves to indicate
9822
- // a "primary" relationship that can be checked as an optimization ahead of a full structural
9823
- // comparison. The function returns the emptyArray singleton if we're not in strictFunctionTypes
9824
- // mode or if the function has been invoked recursively for the given generic type.
9860
+ // Return an array containing the variance of each type parameter. The variance is effectively
9861
+ // a digest of the type comparisons that occur for each type argument when instantiations of the
9862
+ // generic type are structurally compared. We infer the variance information by comparing
9863
+ // instantiations of the generic type for type arguments with known relations. The function
9864
+ // returns the emptyArray singleton if we're not in strictFunctionTypes mode or if the function
9865
+ // has been invoked recursively for the given generic type.
9825
9866
function getVariances(type: GenericType): Variance[] {
9826
9867
if (!strictFunctionTypes) {
9827
9868
return emptyArray;
@@ -9838,14 +9879,20 @@ namespace ts {
9838
9879
type.variances = emptyArray;
9839
9880
variances = [];
9840
9881
for (const tp of typeParameters) {
9841
- // We compare instantiations where the type parameter is replaced with marker types
9842
- // that have a known subtype relationship. From this we infer covariance, contravariance
9843
- // or invariance .
9882
+ // We first compare instantiations where the type parameter is replaced with
9883
+ // marker types that have a known subtype relationship. From this we can infer
9884
+ // invariance, covariance, contravariance or bivariance .
9844
9885
const typeWithSuper = getMarkerTypeReference(type, tp, markerSuperType);
9845
9886
const typeWithSub = getMarkerTypeReference(type, tp, markerSubType);
9846
- const variance = isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant :
9847
- isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant :
9848
- Variance.Invariant;
9887
+ let variance = (isTypeAssignableTo(typeWithSub, typeWithSuper) ? Variance.Covariant : 0) |
9888
+ (isTypeAssignableTo(typeWithSuper, typeWithSub) ? Variance.Contravariant : 0);
9889
+ // If the instantiations appear to be related bivariantly it may be because the
9890
+ // type parameter is independent (i.e. it isn't witnessed anywhere in the generic
9891
+ // type). To determine this we compare instantiations where the type parameter is
9892
+ // replaced with marker types that are known to be unrelated.
9893
+ if (variance === Variance.Bivariant && isTypeAssignableTo(getMarkerTypeReference(type, tp, markerOtherType), typeWithSuper)) {
9894
+ variance = Variance.Independent;
9895
+ }
9849
9896
variances.push(variance);
9850
9897
}
9851
9898
}
@@ -9854,6 +9901,17 @@ namespace ts {
9854
9901
return variances;
9855
9902
}
9856
9903
9904
+ // Return true if the given type reference has a 'void' type argument for a covariant type parameter.
9905
+ // See comment at call in recursiveTypeRelatedTo for when this case matters.
9906
+ function hasCovariantVoidArgument(type: TypeReference, variances: Variance[]): boolean {
9907
+ for (let i = 0; i < variances.length; i++) {
9908
+ if (variances[i] === Variance.Covariant && type.typeArguments[i].flags & TypeFlags.Void) {
9909
+ return true;
9910
+ }
9911
+ }
9912
+ return false;
9913
+ }
9914
+
9857
9915
function isUnconstrainedTypeParameter(type: Type) {
9858
9916
return type.flags & TypeFlags.TypeParameter && !getConstraintFromTypeParameter(<TypeParameter>type);
9859
9917
}
0 commit comments