@@ -194,8 +194,6 @@ namespace ts {
194
194
None = 0,
195
195
Source = 1 << 0,
196
196
Target = 1 << 1,
197
- PropertyCheck = 1 << 2,
198
- InPropertyCheck = 1 << 3,
199
197
}
200
198
201
199
const enum RecursionFlags {
@@ -19112,7 +19110,7 @@ namespace ts {
19112
19110
}
19113
19111
}
19114
19112
19115
- const isPerformingCommonPropertyChecks = (relation !== comparableRelation || !(source.flags & TypeFlags.Union) && isLiteralType (source)) &&
19113
+ const isPerformingCommonPropertyChecks = (relation !== comparableRelation || isUnitType (source)) &&
19116
19114
!(intersectionState & IntersectionState.Target) &&
19117
19115
source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType &&
19118
19116
target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) &&
@@ -19139,31 +19137,9 @@ namespace ts {
19139
19137
19140
19138
const skipCaching = source.flags & TypeFlags.Union && (source as UnionType).types.length < 4 && !(target.flags & TypeFlags.Union) ||
19141
19139
target.flags & TypeFlags.Union && (target as UnionType).types.length < 4 && !(source.flags & TypeFlags.StructuredOrInstantiable);
19142
- let result = skipCaching ?
19140
+ const result = skipCaching ?
19143
19141
unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState) :
19144
19142
recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags);
19145
- // For certain combinations involving intersections and optional, excess, or mismatched properties we need
19146
- // an extra property check where the intersection is viewed as a single object. The following are motivating
19147
- // examples that all should be errors, but aren't without this extra property check:
19148
- //
19149
- // let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
19150
- //
19151
- // declare let wrong: { a: { y: string } };
19152
- // let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
19153
- //
19154
- // function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
19155
- // x = y; // Mismatched property in source intersection
19156
- // }
19157
- //
19158
- // We suppress recursive intersection property checks because they can generate lots of work when relating
19159
- // recursive intersections that are structurally similar but not exactly identical. See #37854.
19160
- if (result && !inPropertyCheck && (
19161
- target.flags & TypeFlags.Intersection && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks) ||
19162
- isNonGenericObjectType(target) && !isArrayOrTupleType(target) && source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType && !some((source as IntersectionType).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType)))) {
19163
- inPropertyCheck = true;
19164
- result &= recursiveTypeRelatedTo(source, target, reportErrors, IntersectionState.PropertyCheck, recursionFlags);
19165
- inPropertyCheck = false;
19166
- }
19167
19143
if (result) {
19168
19144
return result;
19169
19145
}
@@ -19567,8 +19543,7 @@ namespace ts {
19567
19543
if (overflow) {
19568
19544
return Ternary.False;
19569
19545
}
19570
- const keyIntersectionState = intersectionState | (inPropertyCheck ? IntersectionState.InPropertyCheck : 0);
19571
- const id = getRelationKey(source, target, keyIntersectionState, relation, /*ingnoreConstraints*/ false);
19546
+ const id = getRelationKey(source, target, intersectionState, relation, /*ingnoreConstraints*/ false);
19572
19547
const entry = relation.get(id);
19573
19548
if (entry !== undefined) {
19574
19549
if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
@@ -19598,7 +19573,7 @@ namespace ts {
19598
19573
// A key that starts with "*" is an indication that we have type references that reference constrained
19599
19574
// type parameters. For such keys we also check against the key we would have gotten if all type parameters
19600
19575
// were unconstrained.
19601
- const broadestEquivalentId = id.startsWith("*") ? getRelationKey(source, target, keyIntersectionState , relation, /*ignoreConstraints*/ true) : undefined;
19576
+ const broadestEquivalentId = id.startsWith("*") ? getRelationKey(source, target, intersectionState , relation, /*ignoreConstraints*/ true) : undefined;
19602
19577
for (let i = 0; i < maybeCount; i++) {
19603
19578
// If source and target are already being compared, consider them related with assumptions
19604
19579
if (id === maybeKeys[i] || broadestEquivalentId && broadestEquivalentId === maybeKeys[i]) {
@@ -19686,7 +19661,7 @@ namespace ts {
19686
19661
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
19687
19662
const saveErrorInfo = captureErrorCalculationState();
19688
19663
let result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState, saveErrorInfo);
19689
- if (!result && (source.flags & TypeFlags.Intersection || source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.Union) ) {
19664
+ if (relation !== identityRelation ) {
19690
19665
// The combined constraint of an intersection type is the intersection of the constraints of
19691
19666
// the constituents. When an intersection type contains instantiable types with union type
19692
19667
// constraints, there are situations where we need to examine the combined constraint. One is
@@ -19700,10 +19675,34 @@ namespace ts {
19700
19675
// needs to have its constraint hoisted into an intersection with said type parameter, this way
19701
19676
// the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
19702
19677
// For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
19703
- const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
19704
- if (constraint && !(constraint.flags & TypeFlags.Never) && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
19705
- // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
19706
- result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
19678
+ if (!result && (source.flags & TypeFlags.Intersection || source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.Union)) {
19679
+ const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
19680
+ if (constraint && !(constraint.flags & TypeFlags.Never) && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
19681
+ // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
19682
+ result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
19683
+ }
19684
+ }
19685
+ // For certain combinations involving intersections and optional, excess, or mismatched properties we need
19686
+ // an extra property check where the intersection is viewed as a single object. The following are motivating
19687
+ // examples that all should be errors, but aren't without this extra property check:
19688
+ //
19689
+ // let obj: { a: { x: string } } & { c: number } = { a: { x: 'hello', y: 2 }, c: 5 }; // Nested excess property
19690
+ //
19691
+ // declare let wrong: { a: { y: string } };
19692
+ // let weak: { a?: { x?: number } } & { c?: string } = wrong; // Nested weak object type
19693
+ //
19694
+ // function foo<T extends object>(x: { a?: string }, y: T & { a: boolean }) {
19695
+ // x = y; // Mismatched property in source intersection
19696
+ // }
19697
+ //
19698
+ // We suppress recursive intersection property checks because they can generate lots of work when relating
19699
+ // recursive intersections that are structurally similar but not exactly identical. See #37854.
19700
+ if (result && !inPropertyCheck && (
19701
+ target.flags & TypeFlags.Intersection && !isGenericObjectType(target) && source.flags & (TypeFlags.Object | TypeFlags.Intersection) ||
19702
+ isNonGenericObjectType(target) && !isArrayOrTupleType(target) && source.flags & TypeFlags.Intersection && getApparentType(source).flags & TypeFlags.StructuredType && !some((source as IntersectionType).types, t => !!(getObjectFlags(t) & ObjectFlags.NonInferrableType)))) {
19703
+ inPropertyCheck = true;
19704
+ result &= propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
19705
+ inPropertyCheck = false;
19707
19706
}
19708
19707
}
19709
19708
if (result) {
@@ -19713,9 +19712,6 @@ namespace ts {
19713
19712
}
19714
19713
19715
19714
function structuredTypeRelatedToWorker(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState, saveErrorInfo: ReturnType<typeof captureErrorCalculationState>): Ternary {
19716
- if (intersectionState & IntersectionState.PropertyCheck) {
19717
- return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
19718
- }
19719
19715
let result: Ternary;
19720
19716
let originalErrorInfo: DiagnosticMessageChain | undefined;
19721
19717
let varianceCheckFailed = false;
0 commit comments