@@ -183,10 +183,12 @@ namespace ts {
183
183
NoTupleBoundsCheck = 1 << 3,
184
184
}
185
185
186
- const enum CallbackCheck {
187
- None,
188
- Bivariant,
189
- Strict,
186
+ const enum SignatureCheckMode {
187
+ BivariantCallback = 1 << 0,
188
+ StrictCallback = 1 << 1,
189
+ IgnoreReturnTypes = 1 << 2,
190
+ StrictArity = 1 << 3,
191
+ Callback = BivariantCallback | StrictCallback,
190
192
}
191
193
192
194
const enum MappedTypeModifiers {
@@ -13984,7 +13986,7 @@ namespace ts {
13984
13986
function isSignatureAssignableTo(source: Signature,
13985
13987
target: Signature,
13986
13988
ignoreReturnTypes: boolean): boolean {
13987
- return compareSignaturesRelated(source, target, CallbackCheck.None, ignoreReturnTypes , /*reportErrors*/ false,
13989
+ return compareSignaturesRelated(source, target, ignoreReturnTypes ? SignatureCheckMode.IgnoreReturnTypes : 0 , /*reportErrors*/ false,
13988
13990
/*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable, /*reportUnreliableMarkers*/ undefined) !== Ternary.False;
13989
13991
}
13990
13992
@@ -14004,8 +14006,7 @@ namespace ts {
14004
14006
*/
14005
14007
function compareSignaturesRelated(source: Signature,
14006
14008
target: Signature,
14007
- callbackCheck: CallbackCheck,
14008
- ignoreReturnTypes: boolean,
14009
+ checkMode: SignatureCheckMode,
14009
14010
reportErrors: boolean,
14010
14011
errorReporter: ErrorReporter | undefined,
14011
14012
incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined,
@@ -14021,7 +14022,8 @@ namespace ts {
14021
14022
}
14022
14023
14023
14024
const targetCount = getParameterCount(target);
14024
- if (!hasEffectiveRestParameter(target) && getMinArgumentCount(source) > targetCount) {
14025
+ if (!hasEffectiveRestParameter(target) &&
14026
+ (checkMode & SignatureCheckMode.StrictArity ? getParameterCount(source) : getMinArgumentCount(source)) > targetCount) {
14025
14027
return Ternary.False;
14026
14028
}
14027
14029
@@ -14042,7 +14044,7 @@ namespace ts {
14042
14044
}
14043
14045
14044
14046
const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
14045
- const strictVariance = !callbackCheck && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
14047
+ const strictVariance = !(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
14046
14048
kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor;
14047
14049
let result = Ternary.True;
14048
14050
@@ -14077,14 +14079,14 @@ namespace ts {
14077
14079
// similar to return values, callback parameters are output positions. This means that a Promise<T>,
14078
14080
// where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant)
14079
14081
// with respect to T.
14080
- const sourceSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(sourceType));
14081
- const targetSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(targetType));
14082
+ const sourceSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(sourceType));
14083
+ const targetSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(targetType));
14082
14084
const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) &&
14083
14085
(getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
14084
14086
const related = callbacks ?
14085
14087
// TODO: GH#18217 It will work if they're both `undefined`, but not if only one is
14086
- compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false , reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
14087
- !callbackCheck && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
14088
+ compareSignaturesRelated(targetSig!, sourceSig!, (checkMode & SignatureCheckMode.StrictArity) | ( strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback) , reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
14089
+ !(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
14088
14090
if (!related) {
14089
14091
if (reportErrors) {
14090
14092
errorReporter!(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
@@ -14096,7 +14098,7 @@ namespace ts {
14096
14098
result &= related;
14097
14099
}
14098
14100
14099
- if (!ignoreReturnTypes ) {
14101
+ if (!(checkMode & SignatureCheckMode.IgnoreReturnTypes) ) {
14100
14102
// If a signature resolution is already in-flight, skip issuing a circularity error
14101
14103
// here and just use the `any` type directly
14102
14104
const targetReturnType = isResolvingReturnTypeOfSignature(target) ? anyType
@@ -14127,7 +14129,7 @@ namespace ts {
14127
14129
// When relating callback signatures, we still need to relate return types bi-variantly as otherwise
14128
14130
// the containing type wouldn't be co-variant. For example, interface Foo<T> { add(cb: () => T): void }
14129
14131
// wouldn't be co-variant for T without this rule.
14130
- result &= callbackCheck === CallbackCheck.Bivariant && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
14132
+ result &= checkMode & SignatureCheckMode.BivariantCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
14131
14133
compareTypes(sourceReturnType, targetReturnType, reportErrors);
14132
14134
if (!result && reportErrors && incompatibleErrorReporter) {
14133
14135
incompatibleErrorReporter(sourceReturnType, targetReturnType);
@@ -14299,7 +14301,7 @@ namespace ts {
14299
14301
return true;
14300
14302
}
14301
14303
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
14302
- const related = relation.get(getRelationKey(source, target, /*isIntersectionConstituent*/ false, relation));
14304
+ const related = relation.get(getRelationKey(source, target, /*isIntersectionConstituent*/ false, /*strictArityChecks*/ false, relation));
14303
14305
if (related !== undefined) {
14304
14306
return !!(related & RelationComparisonResult.Succeeded);
14305
14307
}
@@ -14351,6 +14353,7 @@ namespace ts {
14351
14353
let depth = 0;
14352
14354
let expandingFlags = ExpandingFlags.None;
14353
14355
let overflow = false;
14356
+ let strictArityChecks = relation === subtypeRelation;
14354
14357
let overrideNextErrorInfo = 0; // How many `reportRelationError` calls should be skipped in the elaboration pyramid
14355
14358
let lastSkippedInfo: [Type, Type] | undefined;
14356
14359
let incompatibleStack: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] = [];
@@ -15139,7 +15142,7 @@ namespace ts {
15139
15142
if (overflow) {
15140
15143
return Ternary.False;
15141
15144
}
15142
- const id = getRelationKey(source, target, isIntersectionConstituent, relation);
15145
+ const id = getRelationKey(source, target, isIntersectionConstituent, strictArityChecks, relation);
15143
15146
const entry = relation.get(id);
15144
15147
if (entry !== undefined) {
15145
15148
if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
@@ -15486,6 +15489,17 @@ namespace ts {
15486
15489
// to X. Failing both of those we want to check if the aggregation of A and B's members structurally
15487
15490
// relates to X. Thus, we include intersection types on the source side here.
15488
15491
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
15492
+ // When performing strict arity checks for signatures (under the subtype relationship), if source has
15493
+ // every property of target, and target is missing some properties from source, then we disable strict
15494
+ // arity checks for the members. This decreases the chances of "ties" in union subtype reduction. For
15495
+ // example, it enables us to consider { f(): void } a supertype of { f(x?: string): void, g(): void },
15496
+ // which we need for backwards compatibility reasons (specifically, for Object and Number).
15497
+ const saveStrictArityChecks = strictArityChecks;
15498
+ if (strictArityChecks &&
15499
+ !getUnmatchedProperty(source, target, /*requireOptionalProperties*/ true, /*matchDiscriminantProperties*/ false) &&
15500
+ getUnmatchedProperty(target, source, /*requireOptionalProperties*/ true, /*matchDiscriminantProperties*/ false)) {
15501
+ strictArityChecks = false;
15502
+ }
15489
15503
// Report structural errors only if we haven't reported any errors yet
15490
15504
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo.errorInfo && !sourceIsPrimitive;
15491
15505
result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, isIntersectionConstituent);
@@ -15501,6 +15515,7 @@ namespace ts {
15501
15515
}
15502
15516
}
15503
15517
}
15518
+ strictArityChecks = saveStrictArityChecks;
15504
15519
if (varianceCheckFailed && result) {
15505
15520
errorInfo = originalErrorInfo || errorInfo || saveErrorInfo.errorInfo; // Use variance error (there is no structural one) and return false
15506
15521
}
@@ -16045,7 +16060,7 @@ namespace ts {
16045
16060
*/
16046
16061
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
16047
16062
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
16048
- CallbackCheck.None, /*ignoreReturnTypes*/ false , reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
16063
+ strictArityChecks ? SignatureCheckMode.StrictArity : 0 , reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
16049
16064
}
16050
16065
16051
16066
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
@@ -16353,18 +16368,18 @@ namespace ts {
16353
16368
* To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters.
16354
16369
* For other cases, the types ids are used.
16355
16370
*/
16356
- function getRelationKey(source: Type, target: Type, isIntersectionConstituent: boolean, relation: Map<RelationComparisonResult>) {
16371
+ function getRelationKey(source: Type, target: Type, isIntersectionConstituent: boolean, strictArityChecks: boolean, relation: Map<RelationComparisonResult>) {
16357
16372
if (relation === identityRelation && source.id > target.id) {
16358
16373
const temp = source;
16359
16374
source = target;
16360
16375
target = temp;
16361
16376
}
16362
- const intersection = isIntersectionConstituent ? "& " : "";
16377
+ const delimiter = isIntersectionConstituent ? strictArityChecks ? "| " : ";" : strictArityChecks ? "/" : ", ";
16363
16378
if (isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target)) {
16364
16379
const typeParameters: Type[] = [];
16365
- return getTypeReferenceId(<TypeReference>source, typeParameters) + "," + getTypeReferenceId(<TypeReference>target, typeParameters) + intersection ;
16380
+ return getTypeReferenceId(<TypeReference>source, typeParameters) + delimiter + getTypeReferenceId(<TypeReference>target, typeParameters);
16366
16381
}
16367
- return source.id + "," + target.id + intersection ;
16382
+ return source.id + delimiter + target.id;
16368
16383
}
16369
16384
16370
16385
// Invoke the callback for each underlying property symbol of the given symbol and return the first
0 commit comments