@@ -1252,12 +1252,13 @@ export const enum CheckMode {
12521252
12531253/** @internal */
12541254export const enum SignatureCheckMode {
1255- None = 0,
1255+ None = 0,
12561256 BivariantCallback = 1 << 0,
1257- StrictCallback = 1 << 1,
1257+ StrictCallback = 1 << 1,
12581258 IgnoreReturnTypes = 1 << 2,
1259- StrictArity = 1 << 3,
1260- Callback = BivariantCallback | StrictCallback,
1259+ StrictArity = 1 << 3,
1260+ StrictTopSignature = 1 << 4,
1261+ Callback = BivariantCallback | StrictCallback,
12611262}
12621263
12631264const enum IntersectionState {
@@ -19582,12 +19583,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1958219583 type ErrorReporter = (message: DiagnosticMessage, arg0?: string, arg1?: string) => void;
1958319584
1958419585 /**
19585- * Returns true if `s` is `(...args: any[] ) => any` or `(this: any, ...args: any[]) => any`
19586+ * Returns true if `s` is `(...args: A ) => R` where `A` is ` any`, ` any[]`, `never`, or `never[]`, and `R` is ` any` or `unknown`.
1958619587 */
19587- function isAnySignature(s: Signature) {
19588- return !s.typeParameters && (!s.thisParameter || isTypeAny(getTypeOfParameter(s.thisParameter))) && s.parameters.length === 1 &&
19589- signatureHasRestParameter(s) && (getTypeOfParameter(s.parameters[0]) === anyArrayType || isTypeAny(getTypeOfParameter(s.parameters[0]))) &&
19590- isTypeAny(getReturnTypeOfSignature(s));
19588+ function isTopSignature(s: Signature) {
19589+ if (!s.typeParameters && (!s.thisParameter || isTypeAny(getTypeOfParameter(s.thisParameter))) && s.parameters.length === 1 && signatureHasRestParameter(s)) {
19590+ const paramType = getTypeOfParameter(s.parameters[0]);
19591+ const restType = isArrayType(paramType) ? getTypeArguments(paramType)[0] : paramType;
19592+ return !!(restType.flags & (TypeFlags.Any | TypeFlags.Never) && getReturnTypeOfSignature(s).flags & TypeFlags.AnyOrUnknown);
19593+ }
19594+ return false;
1959119595 }
1959219596
1959319597 /**
@@ -19606,9 +19610,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1960619610 return Ternary.True;
1960719611 }
1960819612
19609- if (isAnySignature (target)) {
19613+ if (!(checkMode & SignatureCheckMode.StrictTopSignature && isTopSignature(source)) && isTopSignature (target)) {
1961019614 return Ternary.True;
1961119615 }
19616+ if (checkMode & SignatureCheckMode.StrictTopSignature && isTopSignature(source) && !isTopSignature(target)) {
19617+ return Ternary.False;
19618+ }
1961219619
1961319620 const targetCount = getParameterCount(target);
1961419621 const sourceHasMoreParameters = !hasEffectiveRestParameter(target) &&
@@ -19864,7 +19871,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1986419871 function isSimpleTypeRelatedTo(source: Type, target: Type, relation: Map<string, RelationComparisonResult>, errorReporter?: ErrorReporter) {
1986519872 const s = source.flags;
1986619873 const t = target.flags;
19867- if (t & TypeFlags.AnyOrUnknown || s & TypeFlags.Never || source === wildcardType) return true;
19874+ if (t & TypeFlags.Any || s & TypeFlags.Never || source === wildcardType) return true;
19875+ if (t & TypeFlags.Unknown && !(relation === strictSubtypeRelation && s & TypeFlags.Any)) return true;
1986819876 if (t & TypeFlags.Never) return false;
1986919877 if (s & TypeFlags.StringLike && t & TypeFlags.String) return true;
1987019878 if (s & TypeFlags.StringLiteral && s & TypeFlags.EnumLiteral &&
@@ -21486,8 +21494,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2148621494 return Ternary.False;
2148721495 }
2148821496 }
21489- // Consider a fresh empty object literal type "closed" under the subtype relationship - this way `{} <- {[idx : string]: any} <- fresh({})`
21490- // and not `{} <- fresh({}) <- {[idx: string]: any}`
21497+ // A fresh empty object type is never a subtype of a non-empty object type. This ensures fresh({}) <: { [x : string]: xxx }
21498+ // but not vice-versa. Without this rule, those types would be mutual subtypes.
2149121499 else if ((relation === subtypeRelation || relation === strictSubtypeRelation) && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
2149221500 return Ternary.False;
2149321501 }
@@ -22142,8 +22150,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2214222150 * See signatureAssignableTo, compareSignaturesIdentical
2214322151 */
2214422152 function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, intersectionState: IntersectionState, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
22153+ const checkMode = relation === subtypeRelation ? SignatureCheckMode.StrictTopSignature :
22154+ relation === strictSubtypeRelation ? SignatureCheckMode.StrictTopSignature | SignatureCheckMode.StrictArity :
22155+ SignatureCheckMode.None;
2214522156 return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
22146- relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0 , reportErrors, reportError, incompatibleReporter, isRelatedToWorker, reportUnreliableMapper);
22157+ checkMode , reportErrors, reportError, incompatibleReporter, isRelatedToWorker, reportUnreliableMapper);
2214722158 function isRelatedToWorker(source: Type, target: Type, reportErrors?: boolean) {
2214822159 return isRelatedTo(source, target, RecursionFlags.Both, reportErrors, /*headMessage*/ undefined, intersectionState);
2214922160 }
@@ -22239,8 +22250,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2223922250 if (sourceInfo) {
2224022251 return indexInfoRelatedTo(sourceInfo, targetInfo, reportErrors, intersectionState);
2224122252 }
22242- if (!(intersectionState & IntersectionState.Source) && isObjectTypeWithInferableIndex(source)) {
22243- // Intersection constituents are never considered to have an inferred index signature
22253+ // Intersection constituents are never considered to have an inferred index signature. Also, in the strict subtype relation,
22254+ // only fresh object literals are considered to have inferred index signatures. This ensures { [x: string]: xxx } <: {} but
22255+ // not vice-versa. Without this rule, those types would be mutual strict subtypes.
22256+ if (!(intersectionState & IntersectionState.Source) && (relation !== strictSubtypeRelation || getObjectFlags(source) & ObjectFlags.FreshLiteral) && isObjectTypeWithInferableIndex(source)) {
2224422257 return membersRelatedToIndexInfo(source, targetInfo, reportErrors, intersectionState);
2224522258 }
2224622259 if (reportErrors) {
@@ -27089,21 +27102,25 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2708927102 return emptyObjectType;
2709027103 }
2709127104
27092- function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {
27105+ function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean): Type {
2709327106 const key = type.flags & TypeFlags.Union ? `N${getTypeId(type)},${getTypeId(candidate)},${(assumeTrue ? 1 : 0) | (checkDerived ? 2 : 0)}` : undefined;
2709427107 return getCachedType(key) ?? setCachedType(key, getNarrowedTypeWorker(type, candidate, assumeTrue, checkDerived));
2709527108 }
2709627109
2709727110 function getNarrowedTypeWorker(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {
27098- const isRelated = checkDerived ? isTypeDerivedFrom : isTypeSubtypeOf;
2709927111 if (!assumeTrue) {
27100- return filterType(type, t => !isRelated(t, candidate));
27112+ if (checkDerived) {
27113+ return filterType(type, t => !isTypeDerivedFrom(t, candidate));
27114+ }
27115+ const trueType = getNarrowedType(type, candidate, /*assumeTrue*/ true, /*checkDerived*/ false);
27116+ return filterType(type, t => !isTypeSubsetOf(t, trueType));
2710127117 }
2710227118 if (type.flags & TypeFlags.AnyOrUnknown) {
2710327119 return candidate;
2710427120 }
2710527121 // We first attempt to filter the current type, narrowing constituents as appropriate and removing
2710627122 // constituents that are unrelated to the candidate.
27123+ const isRelated = checkDerived ? isTypeDerivedFrom : isTypeSubtypeOf;
2710727124 const keyPropertyName = type.flags & TypeFlags.Union ? getKeyPropertyName(type as UnionType) : undefined;
2710827125 const narrowedType = mapType(candidate, c => {
2710927126 // If a discriminant property is available, use that to reduce the type.
@@ -27115,7 +27132,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2711527132 // prototype object types.
2711627133 const directlyRelated = mapType(matching || type, checkDerived ?
2711727134 t => isTypeDerivedFrom(t, c) ? t : isTypeDerivedFrom(c, t) ? c : neverType :
27118- t => isTypeSubtypeOf(c, t) ? c : isTypeSubtypeOf(t, c) ? t : neverType);
27135+ t => isTypeSubtypeOf(c, t) && !isTypeIdenticalTo(c, t) ? c : isTypeSubtypeOf(t, c) ? t : neverType);
2711927136 // If no constituents are directly related, create intersections for any generic constituents that
2712027137 // are related by constraint.
2712127138 return directlyRelated.flags & TypeFlags.Never ?
@@ -36529,7 +36546,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3652936546 }
3653036547 else {
3653136548 checkAssignmentOperator(rightType);
36532- return getRegularTypeOfObjectLiteral( rightType) ;
36549+ return rightType;
3653336550 }
3653436551 case SyntaxKind.CommaToken:
3653536552 if (!compilerOptions.allowUnreachableCode && isSideEffectFree(left) && !isIndirectCall(left.parent as BinaryExpression)) {
0 commit comments