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:
Anders Hejlsberg
2020-03-12 09:40:15 -07:00
committed by GitHub
parent 9e97b00ca1
commit 41a80f5d92
5 changed files with 100 additions and 6 deletions

View File

@@ -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);
}

View 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);

View File

@@ -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))

View File

@@ -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[]

View 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);