Skip to content

Commit 189fc51

Browse files
committed
Check callback return values bi-variantly
1 parent 50f84b1 commit 189fc51

File tree

1 file changed

+19
-11
lines changed

1 file changed

+19
-11
lines changed

src/compiler/checker.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8157,7 +8157,7 @@ namespace ts {
81578157
function isSignatureAssignableTo(source: Signature,
81588158
target: Signature,
81598159
ignoreReturnTypes: boolean): boolean {
8160-
return compareSignaturesRelated(source, target, /*strictVariance*/ false, ignoreReturnTypes, /*reportErrors*/ false,
8160+
return compareSignaturesRelated(source, target, /*checkAsCallback*/ false, ignoreReturnTypes, /*reportErrors*/ false,
81618161
/*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
81628162
}
81638163

@@ -8168,7 +8168,7 @@ namespace ts {
81688168
*/
81698169
function compareSignaturesRelated(source: Signature,
81708170
target: Signature,
8171-
strictVariance: boolean,
8171+
checkAsCallback: boolean,
81728172
ignoreReturnTypes: boolean,
81738173
reportErrors: boolean,
81748174
errorReporter: ErrorReporter,
@@ -8215,13 +8215,17 @@ namespace ts {
82158215
const targetType = i < targetMax ? getTypeOfParameter(targetParams[i]) : getRestTypeOfSignature(target);
82168216
const sourceSig = getSingleCallSignature(sourceType);
82178217
const targetSig = getSingleCallSignature(targetType);
8218-
// If the source and target parameters both have function types with a single call signature we are
8219-
// relating two callback parameters. In that case we compare the callback signatures with strict
8220-
// variance, meaning we require the callback parameters to be pairwise co-variant (because, similar
8221-
// to return values, callback parameters are output positions).
8222-
const related = sourceSig && targetSig ?
8223-
compareSignaturesRelated(targetSig, sourceSig, /*strictVariance*/ true, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) :
8224-
!strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
8218+
// In order to ensure that any generic type Foo<T> is at least co-variant with respect to T no matter
8219+
// how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions,
8220+
// they naturally relate only contra-variantly). However, if the source and target parameters both have
8221+
// function types with a single call signature, we known we are relating two callback parameters. In
8222+
// that case it is sufficient to only relate the parameters of the signatures co-variantly because,
8223+
// similar to return values, callback parameters are output positions. This means that a Promise<T>,
8224+
// where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant)
8225+
// with respect to T.
8226+
const related = sourceSig && targetSig && !sourceSig.typePredicate && !targetSig.typePredicate ?
8227+
compareSignaturesRelated(targetSig, sourceSig, /*checkAsCallback*/ true, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) :
8228+
!checkAsCallback && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
82258229
if (!related) {
82268230
if (reportErrors) {
82278231
errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
@@ -8253,7 +8257,11 @@ namespace ts {
82538257
}
82548258
}
82558259
else {
8256-
result &= compareTypes(sourceReturnType, targetReturnType, reportErrors);
8260+
// When relating callback signatures, we still need to relate return types bi-variantly as otherwise
8261+
// the containing type wouldn't be co-variant. For example, interface Foo<T> { add(cb: () => T): void }
8262+
// wouldn't be co-variant for T without this rule.
8263+
result &= checkAsCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
8264+
compareTypes(sourceReturnType, targetReturnType, reportErrors);
82578265
}
82588266

82598267
}
@@ -9220,7 +9228,7 @@ namespace ts {
92209228
* See signatureAssignableTo, compareSignaturesIdentical
92219229
*/
92229230
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
9223-
return compareSignaturesRelated(source, target, /*strictVariance*/ false, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
9231+
return compareSignaturesRelated(source, target, /*checkAsCallback*/ false, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
92249232
}
92259233

92269234
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {

0 commit comments

Comments
 (0)