@@ -12882,7 +12882,7 @@ namespace ts {
1288212882 // and we need to handle "each" relations before "some" relations for the same kind of type.
1288312883 if (source.flags & TypeFlags.Union) {
1288412884 result = relation === comparableRelation ?
12885- someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)) :
12885+ someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), isIntersectionConstituent ) :
1288612886 eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive));
1288712887 }
1288812888 else {
@@ -12920,7 +12920,7 @@ namespace ts {
1292012920 //
1292112921 // - For a primitive type or type parameter (such as 'number = A & B') there is no point in
1292212922 // breaking the intersection apart.
12923- result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false);
12923+ result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ true );
1292412924 }
1292512925 if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) {
1292612926 if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent)) {
@@ -13199,14 +13199,14 @@ namespace ts {
1319913199 return result;
1320013200 }
1320113201
13202- function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
13202+ function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean ): Ternary {
1320313203 const sourceTypes = source.types;
1320413204 if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) {
1320513205 return Ternary.True;
1320613206 }
1320713207 const len = sourceTypes.length;
1320813208 for (let i = 0; i < len; i++) {
13209- const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1);
13209+ const related = isRelatedTo(sourceTypes[i], target, reportErrors && i === len - 1, /*headMessage*/ undefined, isIntersectionConstituent );
1321013210 if (related) {
1321113211 return related;
1321213212 }
@@ -14546,6 +14546,9 @@ namespace ts {
1454614546 // though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
1454714547 // expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5
1454814548 // levels, but unequal at some level beyond that.
14549+ // In addition, this will also detect when an indexed access has been chained off of 5 or more times (which is essentially
14550+ // the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding false positives
14551+ // for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`).
1454914552 function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
1455014553 // We track all object types that have an associated symbol (representing the origin of the type)
1455114554 if (depth >= 5 && type.flags & TypeFlags.Object) {
@@ -14561,9 +14564,31 @@ namespace ts {
1456114564 }
1456214565 }
1456314566 }
14567+ if (depth >= 5 && type.flags & TypeFlags.IndexedAccess) {
14568+ const root = getRootObjectTypeFromIndexedAccessChain(type);
14569+ let count = 0;
14570+ for (let i = 0; i < depth; i++) {
14571+ const t = stack[i];
14572+ if (getRootObjectTypeFromIndexedAccessChain(t) === root) {
14573+ count++;
14574+ if (count >= 5) return true;
14575+ }
14576+ }
14577+ }
1456414578 return false;
1456514579 }
1456614580
14581+ /**
14582+ * Gets the leftmost object type in a chain of indexed accesses, eg, in A[P][Q], returns A
14583+ */
14584+ function getRootObjectTypeFromIndexedAccessChain(type: Type) {
14585+ let t = type;
14586+ while (t.flags & TypeFlags.IndexedAccess) {
14587+ t = (t as IndexedAccessType).objectType;
14588+ }
14589+ return t;
14590+ }
14591+
1456714592 function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
1456814593 return compareProperties(sourceProp, targetProp, compareTypesIdentical) !== Ternary.False;
1456914594 }
@@ -15584,11 +15609,14 @@ namespace ts {
1558415609 if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy)) return;
1558515610 }
1558615611 }
15587- else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types, t => !!getInferenceInfoForType(t))) {
15612+ else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types,
15613+ t => !!getInferenceInfoForType(t) || (isGenericMappedType(t) && !!getInferenceInfoForType(getHomomorphicTypeVariable(t) || neverType)))) {
1558815614 // We reduce intersection types only when they contain naked type parameters. For example, when
1558915615 // inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and
1559015616 // infer { extra: any } for T. But when inferring to 'string[] & Iterable<T>' we want to keep the
1559115617 // string[] on the source side and infer string for T.
15618+ // Likewise, we consider a homomorphic mapped type constrainted to the target type parameter as similar to a "naked type variable"
15619+ // in such scenarios.
1559215620 if (source.flags & TypeFlags.Intersection) {
1559315621 // Infer between identically matching source and target constituents and remove the matching types.
1559415622 const [sources, targets] = inferFromMatchingTypes((<IntersectionType>source).types, (<IntersectionType>target).types, isTypeIdenticalTo);
@@ -21574,13 +21602,13 @@ namespace ts {
2157421602 checkMode: CheckMode,
2157521603 reportErrors: boolean,
2157621604 containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
21577- ) {
21605+ ): ReadonlyArray<Diagnostic> | undefined {
2157821606
2157921607 const errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } = { errors: undefined, skipLogging: true };
2158021608 if (isJsxOpeningLikeElement(node)) {
2158121609 if (!checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors, containingMessageChain, errorOutputContainer)) {
2158221610 Debug.assert(!reportErrors || !!errorOutputContainer.errors, "jsx should have errors when reporting errors");
21583- return errorOutputContainer.errors || [] ;
21611+ return errorOutputContainer.errors || emptyArray ;
2158421612 }
2158521613 return undefined;
2158621614 }
@@ -21595,7 +21623,7 @@ namespace ts {
2159521623 const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1;
2159621624 if (!checkTypeRelatedTo(thisArgumentType, thisType, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer)) {
2159721625 Debug.assert(!reportErrors || !!errorOutputContainer.errors, "this parameter should have errors when reporting errors");
21598- return errorOutputContainer.errors || [] ;
21626+ return errorOutputContainer.errors || emptyArray ;
2159921627 }
2160021628 }
2160121629 const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1;
@@ -21613,7 +21641,7 @@ namespace ts {
2161321641 if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage, containingMessageChain, errorOutputContainer)) {
2161421642 Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors");
2161521643 maybeAddMissingAwaitInfo(arg, checkArgType, paramType);
21616- return errorOutputContainer.errors || [] ;
21644+ return errorOutputContainer.errors || emptyArray ;
2161721645 }
2161821646 }
2161921647 }
@@ -21623,7 +21651,7 @@ namespace ts {
2162321651 if (!checkTypeRelatedTo(spreadType, restType, relation, errorNode, headMessage, /*containingMessageChain*/ undefined, errorOutputContainer)) {
2162421652 Debug.assert(!reportErrors || !!errorOutputContainer.errors, "rest parameter should have errors when reporting errors");
2162521653 maybeAddMissingAwaitInfo(errorNode, spreadType, restType);
21626- return errorOutputContainer.errors || [] ;
21654+ return errorOutputContainer.errors || emptyArray ;
2162721655 }
2162821656 }
2162921657 return undefined;
@@ -22014,7 +22042,7 @@ namespace ts {
2201422042 }
2201522043 }
2201622044 else {
22017- const allDiagnostics: DiagnosticRelatedInformation[][] = [];
22045+ const allDiagnostics: (readonly DiagnosticRelatedInformation[]) [] = [];
2201822046 let max = 0;
2201922047 let min = Number.MAX_VALUE;
2202022048 let minIndex = 0;
0 commit comments