@@ -183,10 +183,12 @@ namespace ts {
183183 NoTupleBoundsCheck = 1 << 3,
184184 }
185185
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,
190192 }
191193
192194 const enum MappedTypeModifiers {
@@ -343,6 +345,7 @@ namespace ts {
343345 assignable: assignableRelation.size,
344346 identity: identityRelation.size,
345347 subtype: subtypeRelation.size,
348+ strictSubtype: strictSubtypeRelation.size,
346349 }),
347350 isUndefinedSymbol: symbol => symbol === undefinedSymbol,
348351 isArgumentsSymbol: symbol => symbol === argumentsSymbol,
@@ -869,6 +872,7 @@ namespace ts {
869872 let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
870873
871874 const subtypeRelation = createMap<RelationComparisonResult>();
875+ const strictSubtypeRelation = createMap<RelationComparisonResult>();
872876 const assignableRelation = createMap<RelationComparisonResult>();
873877 const comparableRelation = createMap<RelationComparisonResult>();
874878 const identityRelation = createMap<RelationComparisonResult>();
@@ -11428,7 +11432,7 @@ namespace ts {
1142811432 }
1142911433 }
1143011434 count++;
11431- if (isTypeSubtypeOf (source, target) && (
11435+ if (isTypeRelatedTo (source, target, strictSubtypeRelation ) && (
1143211436 !(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) ||
1143311437 !(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) ||
1143411438 isTypeDerivedFrom(source, target))) {
@@ -14016,7 +14020,7 @@ namespace ts {
1401614020 function isSignatureAssignableTo(source: Signature,
1401714021 target: Signature,
1401814022 ignoreReturnTypes: boolean): boolean {
14019- return compareSignaturesRelated(source, target, CallbackCheck.None, ignoreReturnTypes , /*reportErrors*/ false,
14023+ return compareSignaturesRelated(source, target, ignoreReturnTypes ? SignatureCheckMode.IgnoreReturnTypes : 0 , /*reportErrors*/ false,
1402014024 /*errorReporter*/ undefined, /*errorReporter*/ undefined, compareTypesAssignable, /*reportUnreliableMarkers*/ undefined) !== Ternary.False;
1402114025 }
1402214026
@@ -14036,8 +14040,7 @@ namespace ts {
1403614040 */
1403714041 function compareSignaturesRelated(source: Signature,
1403814042 target: Signature,
14039- callbackCheck: CallbackCheck,
14040- ignoreReturnTypes: boolean,
14043+ checkMode: SignatureCheckMode,
1404114044 reportErrors: boolean,
1404214045 errorReporter: ErrorReporter | undefined,
1404314046 incompatibleErrorReporter: ((source: Type, target: Type) => void) | undefined,
@@ -14053,7 +14056,9 @@ namespace ts {
1405314056 }
1405414057
1405514058 const targetCount = getParameterCount(target);
14056- if (!hasEffectiveRestParameter(target) && getMinArgumentCount(source) > targetCount) {
14059+ const sourceHasMoreParameters = !hasEffectiveRestParameter(target) &&
14060+ (checkMode & SignatureCheckMode.StrictArity ? hasEffectiveRestParameter(source) || getParameterCount(source) > targetCount : getMinArgumentCount(source) > targetCount);
14061+ if (sourceHasMoreParameters) {
1405714062 return Ternary.False;
1405814063 }
1405914064
@@ -14074,7 +14079,7 @@ namespace ts {
1407414079 }
1407514080
1407614081 const kind = target.declaration ? target.declaration.kind : SyntaxKind.Unknown;
14077- const strictVariance = !callbackCheck && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
14082+ const strictVariance = !(checkMode & SignatureCheckMode.Callback) && strictFunctionTypes && kind !== SyntaxKind.MethodDeclaration &&
1407814083 kind !== SyntaxKind.MethodSignature && kind !== SyntaxKind.Constructor;
1407914084 let result = Ternary.True;
1408014085
@@ -14109,14 +14114,17 @@ namespace ts {
1410914114 // similar to return values, callback parameters are output positions. This means that a Promise<T>,
1411014115 // where T is used only in callback parameter positions, will be co-variant (as opposed to bi-variant)
1411114116 // with respect to T.
14112- const sourceSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(sourceType));
14113- const targetSig = callbackCheck ? undefined : getSingleCallSignature(getNonNullableType(targetType));
14117+ const sourceSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(sourceType));
14118+ const targetSig = checkMode & SignatureCheckMode.Callback ? undefined : getSingleCallSignature(getNonNullableType(targetType));
1411414119 const callbacks = sourceSig && targetSig && !getTypePredicateOfSignature(sourceSig) && !getTypePredicateOfSignature(targetSig) &&
1411514120 (getFalsyFlags(sourceType) & TypeFlags.Nullable) === (getFalsyFlags(targetType) & TypeFlags.Nullable);
14116- const related = callbacks ?
14117- // TODO: GH#18217 It will work if they're both `undefined`, but not if only one is
14118- compareSignaturesRelated(targetSig!, sourceSig!, strictVariance ? CallbackCheck.Strict : CallbackCheck.Bivariant, /*ignoreReturnTypes*/ false, reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
14119- !callbackCheck && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
14121+ let related = callbacks ?
14122+ compareSignaturesRelated(targetSig!, sourceSig!, (checkMode & SignatureCheckMode.StrictArity) | (strictVariance ? SignatureCheckMode.StrictCallback : SignatureCheckMode.BivariantCallback), reportErrors, errorReporter, incompatibleErrorReporter, compareTypes, reportUnreliableMarkers) :
14123+ !(checkMode & SignatureCheckMode.Callback) && !strictVariance && compareTypes(sourceType, targetType, /*reportErrors*/ false) || compareTypes(targetType, sourceType, reportErrors);
14124+ // With strict arity, (x: number | undefined) => void is a subtype of (x?: number | undefined) => void
14125+ if (related && checkMode & SignatureCheckMode.StrictArity && i >= getMinArgumentCount(source) && i < getMinArgumentCount(target) && compareTypes(sourceType, targetType, /*reportErrors*/ false)) {
14126+ related = Ternary.False;
14127+ }
1412014128 if (!related) {
1412114129 if (reportErrors) {
1412214130 errorReporter!(Diagnostics.Types_of_parameters_0_and_1_are_incompatible,
@@ -14128,7 +14136,7 @@ namespace ts {
1412814136 result &= related;
1412914137 }
1413014138
14131- if (!ignoreReturnTypes ) {
14139+ if (!(checkMode & SignatureCheckMode.IgnoreReturnTypes) ) {
1413214140 // If a signature resolution is already in-flight, skip issuing a circularity error
1413314141 // here and just use the `any` type directly
1413414142 const targetReturnType = isResolvingReturnTypeOfSignature(target) ? anyType
@@ -14159,7 +14167,7 @@ namespace ts {
1415914167 // When relating callback signatures, we still need to relate return types bi-variantly as otherwise
1416014168 // the containing type wouldn't be co-variant. For example, interface Foo<T> { add(cb: () => T): void }
1416114169 // wouldn't be co-variant for T without this rule.
14162- result &= callbackCheck === CallbackCheck.Bivariant && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
14170+ result &= checkMode & SignatureCheckMode.BivariantCallback && compareTypes(targetReturnType, sourceReturnType, /*reportErrors*/ false) ||
1416314171 compareTypes(sourceReturnType, targetReturnType, reportErrors);
1416414172 if (!result && reportErrors && incompatibleErrorReporter) {
1416514173 incompatibleErrorReporter(sourceReturnType, targetReturnType);
@@ -15467,7 +15475,7 @@ namespace ts {
1546715475 }
1546815476 else {
1546915477 // An empty object type is related to any mapped type that includes a '?' modifier.
15470- if (relation !== subtypeRelation && isPartialMappedType(target) && isEmptyObjectType(source)) {
15478+ if (relation !== subtypeRelation && relation !== strictSubtypeRelation && isPartialMappedType(target) && isEmptyObjectType(source)) {
1547115479 return Ternary.True;
1547215480 }
1547315481 if (isGenericMappedType(target)) {
@@ -15509,7 +15517,7 @@ namespace ts {
1550915517 }
1551015518 // Consider a fresh empty object literal type "closed" under the subtype relationship - this way `{} <- {[idx: string]: any} <- fresh({})`
1551115519 // and not `{} <- fresh({}) <- {[idx: string]: any}`
15512- else if (relation === subtypeRelation && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
15520+ else if (( relation === subtypeRelation || relation === strictSubtypeRelation) && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
1551315521 return Ternary.False;
1551415522 }
1551515523 // Even if relationship doesn't hold for unions, intersections, or generic type references,
@@ -15854,7 +15862,7 @@ namespace ts {
1585415862 if (relation === identityRelation) {
1585515863 return propertiesIdenticalTo(source, target, excludedProperties);
1585615864 }
15857- const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source);
15865+ const requireOptionalProperties = ( relation === subtypeRelation || relation === strictSubtypeRelation) && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source);
1585815866 const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false);
1585915867 if (unmatchedProperty) {
1586015868 if (reportErrors) {
@@ -16077,7 +16085,7 @@ namespace ts {
1607716085 */
1607816086 function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
1607916087 return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
16080- CallbackCheck.None, /*ignoreReturnTypes*/ false , reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
16088+ relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0 , reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
1608116089 }
1608216090
1608316091 function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
@@ -16391,12 +16399,12 @@ namespace ts {
1639116399 source = target;
1639216400 target = temp;
1639316401 }
16394- const intersection = isIntersectionConstituent ? "& " : "";
16402+ const delimiter = isIntersectionConstituent ? "; " : ", ";
1639516403 if (isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target)) {
1639616404 const typeParameters: Type[] = [];
16397- return getTypeReferenceId(<TypeReference>source, typeParameters) + "," + getTypeReferenceId(<TypeReference>target, typeParameters) + intersection ;
16405+ return getTypeReferenceId(<TypeReference>source, typeParameters) + delimiter + getTypeReferenceId(<TypeReference>target, typeParameters);
1639816406 }
16399- return source.id + "," + target.id + intersection ;
16407+ return source.id + delimiter + target.id;
1640016408 }
1640116409
1640216410 // Invoke the callback for each underlying property symbol of the given symbol and return the first
0 commit comments