@@ -12882,7 +12882,7 @@ namespace ts {
12882
12882
// and we need to handle "each" relations before "some" relations for the same kind of type.
12883
12883
if (source.flags & TypeFlags.Union) {
12884
12884
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 ) :
12886
12886
eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive));
12887
12887
}
12888
12888
else {
@@ -12920,7 +12920,7 @@ namespace ts {
12920
12920
//
12921
12921
// - For a primitive type or type parameter (such as 'number = A & B') there is no point in
12922
12922
// breaking the intersection apart.
12923
- result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false);
12923
+ result = someTypeRelatedToType(<IntersectionType>source, target, /*reportErrors*/ false, /*isIntersectionConstituent*/ true );
12924
12924
}
12925
12925
if (!result && (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable)) {
12926
12926
if (result = recursiveTypeRelatedTo(source, target, reportErrors, isIntersectionConstituent)) {
@@ -13199,14 +13199,14 @@ namespace ts {
13199
13199
return result;
13200
13200
}
13201
13201
13202
- function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary {
13202
+ function someTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, isIntersectionConstituent: boolean ): Ternary {
13203
13203
const sourceTypes = source.types;
13204
13204
if (source.flags & TypeFlags.Union && containsType(sourceTypes, target)) {
13205
13205
return Ternary.True;
13206
13206
}
13207
13207
const len = sourceTypes.length;
13208
13208
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 );
13210
13210
if (related) {
13211
13211
return related;
13212
13212
}
@@ -14546,6 +14546,9 @@ namespace ts {
14546
14546
// though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
14547
14547
// expanding. Effectively, we will generate a false positive when two types are structurally equal to at least 5
14548
14548
// 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]`).
14549
14552
function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
14550
14553
// We track all object types that have an associated symbol (representing the origin of the type)
14551
14554
if (depth >= 5 && type.flags & TypeFlags.Object) {
@@ -14561,9 +14564,31 @@ namespace ts {
14561
14564
}
14562
14565
}
14563
14566
}
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
+ }
14564
14578
return false;
14565
14579
}
14566
14580
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
+
14567
14592
function isPropertyIdenticalTo(sourceProp: Symbol, targetProp: Symbol): boolean {
14568
14593
return compareProperties(sourceProp, targetProp, compareTypesIdentical) !== Ternary.False;
14569
14594
}
@@ -15584,11 +15609,14 @@ namespace ts {
15584
15609
if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy)) return;
15585
15610
}
15586
15611
}
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)))) {
15588
15614
// We reduce intersection types only when they contain naked type parameters. For example, when
15589
15615
// inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and
15590
15616
// infer { extra: any } for T. But when inferring to 'string[] & Iterable<T>' we want to keep the
15591
15617
// 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.
15592
15620
if (source.flags & TypeFlags.Intersection) {
15593
15621
// Infer between identically matching source and target constituents and remove the matching types.
15594
15622
const [sources, targets] = inferFromMatchingTypes((<IntersectionType>source).types, (<IntersectionType>target).types, isTypeIdenticalTo);
@@ -21574,13 +21602,13 @@ namespace ts {
21574
21602
checkMode: CheckMode,
21575
21603
reportErrors: boolean,
21576
21604
containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined,
21577
- ) {
21605
+ ): ReadonlyArray<Diagnostic> | undefined {
21578
21606
21579
21607
const errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } = { errors: undefined, skipLogging: true };
21580
21608
if (isJsxOpeningLikeElement(node)) {
21581
21609
if (!checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors, containingMessageChain, errorOutputContainer)) {
21582
21610
Debug.assert(!reportErrors || !!errorOutputContainer.errors, "jsx should have errors when reporting errors");
21583
- return errorOutputContainer.errors || [] ;
21611
+ return errorOutputContainer.errors || emptyArray ;
21584
21612
}
21585
21613
return undefined;
21586
21614
}
@@ -21595,7 +21623,7 @@ namespace ts {
21595
21623
const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1;
21596
21624
if (!checkTypeRelatedTo(thisArgumentType, thisType, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer)) {
21597
21625
Debug.assert(!reportErrors || !!errorOutputContainer.errors, "this parameter should have errors when reporting errors");
21598
- return errorOutputContainer.errors || [] ;
21626
+ return errorOutputContainer.errors || emptyArray ;
21599
21627
}
21600
21628
}
21601
21629
const headMessage = Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1;
@@ -21613,7 +21641,7 @@ namespace ts {
21613
21641
if (!checkTypeRelatedToAndOptionallyElaborate(checkArgType, paramType, relation, reportErrors ? arg : undefined, arg, headMessage, containingMessageChain, errorOutputContainer)) {
21614
21642
Debug.assert(!reportErrors || !!errorOutputContainer.errors, "parameter should have errors when reporting errors");
21615
21643
maybeAddMissingAwaitInfo(arg, checkArgType, paramType);
21616
- return errorOutputContainer.errors || [] ;
21644
+ return errorOutputContainer.errors || emptyArray ;
21617
21645
}
21618
21646
}
21619
21647
}
@@ -21623,7 +21651,7 @@ namespace ts {
21623
21651
if (!checkTypeRelatedTo(spreadType, restType, relation, errorNode, headMessage, /*containingMessageChain*/ undefined, errorOutputContainer)) {
21624
21652
Debug.assert(!reportErrors || !!errorOutputContainer.errors, "rest parameter should have errors when reporting errors");
21625
21653
maybeAddMissingAwaitInfo(errorNode, spreadType, restType);
21626
- return errorOutputContainer.errors || [] ;
21654
+ return errorOutputContainer.errors || emptyArray ;
21627
21655
}
21628
21656
}
21629
21657
return undefined;
@@ -22014,7 +22042,7 @@ namespace ts {
22014
22042
}
22015
22043
}
22016
22044
else {
22017
- const allDiagnostics: DiagnosticRelatedInformation[][] = [];
22045
+ const allDiagnostics: (readonly DiagnosticRelatedInformation[]) [] = [];
22018
22046
let max = 0;
22019
22047
let min = Number.MAX_VALUE;
22020
22048
let minIndex = 0;
0 commit comments