mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-06 15:03:14 -05:00
Merge pull request #15104 from Microsoft/covariantCallbacks
Covariant checking for callback parameters
This commit is contained in:
@@ -8206,7 +8206,8 @@ namespace ts {
|
||||
function isSignatureAssignableTo(source: Signature,
|
||||
target: Signature,
|
||||
ignoreReturnTypes: boolean): boolean {
|
||||
return compareSignaturesRelated(source, target, ignoreReturnTypes, /*reportErrors*/ false, /*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
|
||||
return compareSignaturesRelated(source, target, /*checkAsCallback*/ false, ignoreReturnTypes, /*reportErrors*/ false,
|
||||
/*errorReporter*/ undefined, compareTypesAssignable) !== Ternary.False;
|
||||
}
|
||||
|
||||
type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
|
||||
@@ -8216,6 +8217,7 @@ namespace ts {
|
||||
*/
|
||||
function compareSignaturesRelated(source: Signature,
|
||||
target: Signature,
|
||||
checkAsCallback: boolean,
|
||||
ignoreReturnTypes: boolean,
|
||||
reportErrors: boolean,
|
||||
errorReporter: ErrorReporter,
|
||||
@@ -8258,9 +8260,23 @@ namespace ts {
|
||||
const sourceParams = source.parameters;
|
||||
const targetParams = target.parameters;
|
||||
for (let i = 0; i < checkCount; i++) {
|
||||
const s = i < sourceMax ? getTypeOfParameter(sourceParams[i]) : getRestTypeOfSignature(source);
|
||||
const t = i < targetMax ? getTypeOfParameter(targetParams[i]) : getRestTypeOfSignature(target);
|
||||
const related = compareTypes(s, t, /*reportErrors*/ false) || compareTypes(t, s, reportErrors);
|
||||
const sourceType = i < sourceMax ? getTypeOfParameter(sourceParams[i]) : getRestTypeOfSignature(source);
|
||||
const targetType = i < targetMax ? getTypeOfParameter(targetParams[i]) : getRestTypeOfSignature(target);
|
||||
const sourceSig = getSingleCallSignature(getNonNullableType(sourceType));
|
||||
const targetSig = getSingleCallSignature(getNonNullableType(targetType));
|
||||
// In order to ensure that any generic type Foo<T> is at least co-variant with respect to T no matter
|
||||
// how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions,
|
||||
// they naturally relate only contra-variantly). However, if the source and target parameters both have
|
||||
// function types with a single call signature, we known we are relating two callback parameters. In
|
||||
// that case it is sufficient to only relate the parameters of the signatures co-variantly because,
|
||||
// similar to return values, callback parameters are output positions. This means that a Promise<T>,
|
||||
// where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant)
|
||||
// with respect to T.
|
||||
const callbacks = sourceSig && targetSig && !sourceSig.typePredicate && !targetSig.typePredicate &&
|
||||
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
|
||||
const related = callbacks ?
|
||||
compareSignaturesRelated(targetSig, sourceSig, /*checkAsCallback*/ true, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, compareTypes) :
|
||||
!checkAsCallback && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
|
||||
if (!related) {
|
||||
if (reportErrors) {
|
||||
errorReporter(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
|
||||
@@ -8292,7 +8308,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
else {
|
||||
result &= compareTypes(sourceReturnType, targetReturnType, reportErrors);
|
||||
// When relating callback signatures, we still need to relate return types bi-variantly as otherwise
|
||||
// the containing type wouldn't be co-variant. For example, interface Foo<T> { add(cb: () => T): void }
|
||||
// wouldn't be co-variant for T without this rule.
|
||||
result &= checkAsCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
|
||||
compareTypes(sourceReturnType, targetReturnType, reportErrors);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9260,7 +9280,7 @@ namespace ts {
|
||||
* See signatureAssignableTo, compareSignaturesIdentical
|
||||
*/
|
||||
function signatureRelatedTo(source: Signature, target: Signature, reportErrors: boolean): Ternary {
|
||||
return compareSignaturesRelated(source, target, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
|
||||
return compareSignaturesRelated(source, target, /*checkAsCallback*/ false, /*ignoreReturnTypes*/ false, reportErrors, reportError, isRelatedTo);
|
||||
}
|
||||
|
||||
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
|
||||
|
||||
@@ -57,7 +57,7 @@ namespace ts {
|
||||
// The visitXXX functions could be written as local functions that close over the cbNode and cbNodeArray
|
||||
// callback parameters, but that causes a closure allocation for each invocation with noticeable effects
|
||||
// on performance.
|
||||
const visitNodes: (cb: (node: Node | Node[]) => T, nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode;
|
||||
const visitNodes: (cb: ((node: Node) => T) | ((node: Node[]) => T), nodes: Node[]) => T = cbNodeArray ? visitNodeArray : visitEachNode;
|
||||
const cbNodes = cbNodeArray || cbNode;
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.QualifiedName:
|
||||
|
||||
@@ -885,7 +885,7 @@ namespace ts {
|
||||
return initial;
|
||||
}
|
||||
|
||||
const reduceNodes: (nodes: NodeArray<Node>, f: (memo: T, node: Node | NodeArray<Node>) => T, initial: T) => T = cbNodeArray ? reduceNodeArray : reduceLeft;
|
||||
const reduceNodes: (nodes: NodeArray<Node>, f: ((memo: T, node: Node) => T) | ((memo: T, node: NodeArray<Node>) => T), initial: T) => T = cbNodeArray ? reduceNodeArray : reduceLeft;
|
||||
const cbNodes = cbNodeArray || cbNode;
|
||||
const kind = node.kind;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user