@@ -194,8 +194,6 @@ namespace ts {
194194 None = 0,
195195 Source = 1 << 0,
196196 Target = 1 << 1,
197- PropertyCheck = 1 << 2,
198- InPropertyCheck = 1 << 3,
199197 }
200198
201199 const enum RecursionFlags {
@@ -19112,7 +19110,7 @@ namespace ts {
1911219110 }
1911319111 }
1911419112
19115- const isPerformingCommonPropertyChecks = (relation !== comparableRelation || !(source.flags & TypeFlags.Union) && isLiteralType (source)) &&
19113+ const isPerformingCommonPropertyChecks = (relation !== comparableRelation || isUnitType (source)) &&
1911619114 !(intersectionState & IntersectionState.Target) &&
1911719115 source.flags & (TypeFlags.Primitive | TypeFlags.Object | TypeFlags.Intersection) && source !== globalObjectType &&
1911819116 target.flags & (TypeFlags.Object | TypeFlags.Intersection) && isWeakType(target) &&
@@ -19139,31 +19137,9 @@ namespace ts {
1913919137
1914019138 const skipCaching = source.flags & TypeFlags.Union && (source as UnionType).types.length < 4 && !(target.flags & TypeFlags.Union) ||
1914119139 target.flags & TypeFlags.Union && (target as UnionType).types.length < 4 && !(source.flags & TypeFlags.StructuredOrInstantiable);
19142- let result = skipCaching ?
19140+ const result = skipCaching ?
1914319141 unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState) :
1914419142 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- }
1916719143 if (result) {
1916819144 return result;
1916919145 }
@@ -19567,8 +19543,7 @@ namespace ts {
1956719543 if (overflow) {
1956819544 return Ternary.False;
1956919545 }
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);
1957219547 const entry = relation.get(id);
1957319548 if (entry !== undefined) {
1957419549 if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
@@ -19598,7 +19573,7 @@ namespace ts {
1959819573 // A key that starts with "*" is an indication that we have type references that reference constrained
1959919574 // type parameters. For such keys we also check against the key we would have gotten if all type parameters
1960019575 // 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;
1960219577 for (let i = 0; i < maybeCount; i++) {
1960319578 // If source and target are already being compared, consider them related with assumptions
1960419579 if (id === maybeKeys[i] || broadestEquivalentId && broadestEquivalentId === maybeKeys[i]) {
@@ -19686,7 +19661,7 @@ namespace ts {
1968619661 function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
1968719662 const saveErrorInfo = captureErrorCalculationState();
1968819663 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 ) {
1969019665 // The combined constraint of an intersection type is the intersection of the constraints of
1969119666 // the constituents. When an intersection type contains instantiable types with union type
1969219667 // constraints, there are situations where we need to examine the combined constraint. One is
@@ -19700,10 +19675,34 @@ namespace ts {
1970019675 // needs to have its constraint hoisted into an intersection with said type parameter, this way
1970119676 // the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
1970219677 // 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;
1970719706 }
1970819707 }
1970919708 if (result) {
@@ -19713,9 +19712,6 @@ namespace ts {
1971319712 }
1971419713
1971519714 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- }
1971919715 let result: Ternary;
1972019716 let originalErrorInfo: DiagnosticMessageChain | undefined;
1972119717 let varianceCheckFailed = false;
0 commit comments