mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-26 00:36:29 -05:00
No infinite recursion in excess property and weak type checks (#37360)
* New IntersectionState.ExcessCheck flag to ensure no infinite recursion * Add regression test
This commit is contained in:
@@ -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<true> | undefined, intersectionState: IntersectionState): Ternary {
|
||||
|
||||
if (relation === identityRelation) {
|
||||
return propertiesIdenticalTo(source, target, excludedProperties);
|
||||
}
|
||||
|
||||
23
tests/baselines/reference/recursiveExcessPropertyChecks.js
Normal file
23
tests/baselines/reference/recursiveExcessPropertyChecks.js
Normal file
@@ -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);
|
||||
@@ -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))
|
||||
|
||||
@@ -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[]
|
||||
|
||||
15
tests/cases/compiler/recursiveExcessPropertyChecks.ts
Normal file
15
tests/cases/compiler/recursiveExcessPropertyChecks.ts
Normal file
@@ -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);
|
||||
Reference in New Issue
Block a user