diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 90dbac991aa..a6f0d6dd216 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -195,6 +195,7 @@ namespace ts { None = 0, Source = 1 << 0, Target = 1 << 1, + ExcessCheck = 1 << 2, } const enum MappedTypeModifiers { @@ -15368,7 +15369,7 @@ namespace ts { if (source.flags & TypeFlags.Union) { result = relation === comparableRelation ? someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState) : - eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive)); + eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & IntersectionState.ExcessCheck); } else { if (target.flags & TypeFlags.Union) { @@ -15376,9 +15377,9 @@ namespace ts { } else if (target.flags & TypeFlags.Intersection) { result = typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target); - if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks)) { + if (result && (isPerformingExcessPropertyChecks || isPerformingCommonPropertyChecks) && !(intersectionState & IntersectionState.ExcessCheck)) { // Validate against excess props using the original `source` - if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None)) { + if (!propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.ExcessCheck)) { return Ternary.False; } } @@ -15633,11 +15634,11 @@ namespace ts { return Ternary.False; } - function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean): Ternary { + function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary { let result = Ternary.True; const sourceTypes = source.types; for (const sourceType of sourceTypes) { - const related = isRelatedTo(sourceType, target, reportErrors); + const related = isRelatedTo(sourceType, target, reportErrors, /*headMessage*/ undefined, intersectionState); if (!related) { return Ternary.False; } @@ -16451,7 +16452,6 @@ namespace ts { } function propertiesRelatedTo(source: Type, target: Type, reportErrors: boolean, excludedProperties: UnderscoreEscapedMap | undefined, intersectionState: IntersectionState): Ternary { - if (relation === identityRelation) { return propertiesIdenticalTo(source, target, excludedProperties); } diff --git a/tests/baselines/reference/recursiveExcessPropertyChecks.js b/tests/baselines/reference/recursiveExcessPropertyChecks.js new file mode 100644 index 00000000000..1235b3e93bb --- /dev/null +++ b/tests/baselines/reference/recursiveExcessPropertyChecks.js @@ -0,0 +1,23 @@ +//// [recursiveExcessPropertyChecks.ts] +// Repro from #35804 + +interface ITreeItem { + Parent?: this; +} + +type NodeWithId = ITreeItem & { Id?: number }; + +function getMaxId(items: NodeWithId[]) { +} + +const nodes = [] as ITreeItem[]; +getMaxId(nodes); + + +//// [recursiveExcessPropertyChecks.js] +"use strict"; +// Repro from #35804 +function getMaxId(items) { +} +var nodes = []; +getMaxId(nodes); diff --git a/tests/baselines/reference/recursiveExcessPropertyChecks.symbols b/tests/baselines/reference/recursiveExcessPropertyChecks.symbols new file mode 100644 index 00000000000..0f335f07073 --- /dev/null +++ b/tests/baselines/reference/recursiveExcessPropertyChecks.symbols @@ -0,0 +1,29 @@ +=== tests/cases/compiler/recursiveExcessPropertyChecks.ts === +// Repro from #35804 + +interface ITreeItem { +>ITreeItem : Symbol(ITreeItem, Decl(recursiveExcessPropertyChecks.ts, 0, 0)) + + Parent?: this; +>Parent : Symbol(ITreeItem.Parent, Decl(recursiveExcessPropertyChecks.ts, 2, 21)) +} + +type NodeWithId = ITreeItem & { Id?: number }; +>NodeWithId : Symbol(NodeWithId, Decl(recursiveExcessPropertyChecks.ts, 4, 1)) +>ITreeItem : Symbol(ITreeItem, Decl(recursiveExcessPropertyChecks.ts, 0, 0)) +>Id : Symbol(Id, Decl(recursiveExcessPropertyChecks.ts, 6, 31)) + +function getMaxId(items: NodeWithId[]) { +>getMaxId : Symbol(getMaxId, Decl(recursiveExcessPropertyChecks.ts, 6, 46)) +>items : Symbol(items, Decl(recursiveExcessPropertyChecks.ts, 8, 18)) +>NodeWithId : Symbol(NodeWithId, Decl(recursiveExcessPropertyChecks.ts, 4, 1)) +} + +const nodes = [] as ITreeItem[]; +>nodes : Symbol(nodes, Decl(recursiveExcessPropertyChecks.ts, 11, 5)) +>ITreeItem : Symbol(ITreeItem, Decl(recursiveExcessPropertyChecks.ts, 0, 0)) + +getMaxId(nodes); +>getMaxId : Symbol(getMaxId, Decl(recursiveExcessPropertyChecks.ts, 6, 46)) +>nodes : Symbol(nodes, Decl(recursiveExcessPropertyChecks.ts, 11, 5)) + diff --git a/tests/baselines/reference/recursiveExcessPropertyChecks.types b/tests/baselines/reference/recursiveExcessPropertyChecks.types new file mode 100644 index 00000000000..04b78367460 --- /dev/null +++ b/tests/baselines/reference/recursiveExcessPropertyChecks.types @@ -0,0 +1,27 @@ +=== tests/cases/compiler/recursiveExcessPropertyChecks.ts === +// Repro from #35804 + +interface ITreeItem { + Parent?: this; +>Parent : this | undefined +} + +type NodeWithId = ITreeItem & { Id?: number }; +>NodeWithId : NodeWithId +>Id : number | undefined + +function getMaxId(items: NodeWithId[]) { +>getMaxId : (items: NodeWithId[]) => void +>items : NodeWithId[] +} + +const nodes = [] as ITreeItem[]; +>nodes : ITreeItem[] +>[] as ITreeItem[] : ITreeItem[] +>[] : never[] + +getMaxId(nodes); +>getMaxId(nodes) : void +>getMaxId : (items: NodeWithId[]) => void +>nodes : ITreeItem[] + diff --git a/tests/cases/compiler/recursiveExcessPropertyChecks.ts b/tests/cases/compiler/recursiveExcessPropertyChecks.ts new file mode 100644 index 00000000000..210f6fa6a9f --- /dev/null +++ b/tests/cases/compiler/recursiveExcessPropertyChecks.ts @@ -0,0 +1,15 @@ +// @strict: true + +// Repro from #35804 + +interface ITreeItem { + Parent?: this; +} + +type NodeWithId = ITreeItem & { Id?: number }; + +function getMaxId(items: NodeWithId[]) { +} + +const nodes = [] as ITreeItem[]; +getMaxId(nodes);