From 24bf57a4a97ba110a37149757423f45b797b306b Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 16 Apr 2021 09:18:01 -0700 Subject: [PATCH 1/2] typeRelatedToSomeType passes through intersectionState Previously it didn't, even though it should have. --- src/compiler/checker.ts | 12 +-- .../recursiveExcessPropertyChecks.js | 47 +++++++++++ .../recursiveExcessPropertyChecks.symbols | 77 +++++++++++++++++++ .../recursiveExcessPropertyChecks.types | 72 +++++++++++++++++ .../compiler/recursiveExcessPropertyChecks.ts | 34 ++++++++ 5 files changed, 236 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6e0ed33153f7a..fa7a101928c10 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17859,7 +17859,7 @@ namespace ts { let result = Ternary.True; const sourceTypes = source.types; for (const sourceType of sourceTypes) { - const related = typeRelatedToSomeType(sourceType, target, /*reportErrors*/ false); + const related = typeRelatedToSomeType(sourceType, target, /*reportErrors*/ false, IntersectionState.None); if (!related) { return Ternary.False; } @@ -17868,7 +17868,7 @@ namespace ts { return result; } - function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean): Ternary { + function typeRelatedToSomeType(source: Type, target: UnionOrIntersectionType, reportErrors: boolean, intersectionState: IntersectionState): Ternary { const targetTypes = target.types; if (target.flags & TypeFlags.Union) { if (containsType(targetTypes, source)) { @@ -17876,21 +17876,21 @@ namespace ts { } const match = getMatchingUnionConstituentForType(target, source); if (match) { - const related = isRelatedTo(source, match, /*reportErrors*/ false); + const related = isRelatedTo(source, match, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState); if (related) { return related; } } } for (const type of targetTypes) { - const related = isRelatedTo(source, type, /*reportErrors*/ false); + const related = isRelatedTo(source, type, /*reportErrors*/ false, undefined, intersectionState); if (related) { return related; } } if (reportErrors) { const bestMatchingType = getBestMatchingType(source, target, isRelatedTo); - isRelatedTo(source, bestMatchingType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true); + isRelatedTo(source, bestMatchingType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true, /*headMessage*/ undefined, intersectionState); } return Ternary.False; } @@ -18150,7 +18150,7 @@ namespace ts { eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck); } if (target.flags & TypeFlags.Union) { - return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive)); + return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck); } if (target.flags & TypeFlags.Intersection) { return typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target); diff --git a/tests/baselines/reference/recursiveExcessPropertyChecks.js b/tests/baselines/reference/recursiveExcessPropertyChecks.js index 1235b3e93bb2e..1bc420a8bca98 100644 --- a/tests/baselines/reference/recursiveExcessPropertyChecks.js +++ b/tests/baselines/reference/recursiveExcessPropertyChecks.js @@ -12,12 +12,59 @@ function getMaxId(items: NodeWithId[]) { const nodes = [] as ITreeItem[]; getMaxId(nodes); + + +// Repro from #42715 +export interface Donkey { + donkey: string; +} + +interface Diddy { + diddy: string; + children?: Diddy[] | Donkey; +} + +interface Cranky { + cranky: string; + children: Diddy; +} + +type Dandy = Diddy & { + children: (Diddy & { funky?: string })[]; +}; +type X = Dandy["children"] +var x: X + +const mainView: Dandy = { + diddy: "", + children: [ + { + diddy: "", + funky: "Empty" // <- Incorrect error + } + ], +}; +// Legal +const p = mainView.children[0].funky //// [recursiveExcessPropertyChecks.js] "use strict"; // Repro from #35804 +exports.__esModule = true; function getMaxId(items) { } var nodes = []; getMaxId(nodes); +var x; +var mainView = { + diddy: "", + children: [ + { + diddy: "", + funky: "Empty" // <- Incorrect error + } + ] +}; +// Legal +var p = mainView.children[0].funky; diff --git a/tests/baselines/reference/recursiveExcessPropertyChecks.symbols b/tests/baselines/reference/recursiveExcessPropertyChecks.symbols index 0f335f0707330..a90a1dc93e1a6 100644 --- a/tests/baselines/reference/recursiveExcessPropertyChecks.symbols +++ b/tests/baselines/reference/recursiveExcessPropertyChecks.symbols @@ -27,3 +27,80 @@ getMaxId(nodes); >getMaxId : Symbol(getMaxId, Decl(recursiveExcessPropertyChecks.ts, 6, 46)) >nodes : Symbol(nodes, Decl(recursiveExcessPropertyChecks.ts, 11, 5)) + +// Repro from #42715 +export interface Donkey { +>Donkey : Symbol(Donkey, Decl(recursiveExcessPropertyChecks.ts, 12, 16)) + + donkey: string; +>donkey : Symbol(Donkey.donkey, Decl(recursiveExcessPropertyChecks.ts, 16, 25)) +} + +interface Diddy { +>Diddy : Symbol(Diddy, Decl(recursiveExcessPropertyChecks.ts, 18, 1)) + + diddy: string; +>diddy : Symbol(Diddy.diddy, Decl(recursiveExcessPropertyChecks.ts, 20, 17)) + + children?: Diddy[] | Donkey; +>children : Symbol(Diddy.children, Decl(recursiveExcessPropertyChecks.ts, 21, 18)) +>Diddy : Symbol(Diddy, Decl(recursiveExcessPropertyChecks.ts, 18, 1)) +>Donkey : Symbol(Donkey, Decl(recursiveExcessPropertyChecks.ts, 12, 16)) +} + +interface Cranky { +>Cranky : Symbol(Cranky, Decl(recursiveExcessPropertyChecks.ts, 23, 1)) + + cranky: string; +>cranky : Symbol(Cranky.cranky, Decl(recursiveExcessPropertyChecks.ts, 25, 18)) + + children: Diddy; +>children : Symbol(Cranky.children, Decl(recursiveExcessPropertyChecks.ts, 26, 19)) +>Diddy : Symbol(Diddy, Decl(recursiveExcessPropertyChecks.ts, 18, 1)) +} + +type Dandy = Diddy & { +>Dandy : Symbol(Dandy, Decl(recursiveExcessPropertyChecks.ts, 28, 1)) +>Diddy : Symbol(Diddy, Decl(recursiveExcessPropertyChecks.ts, 18, 1)) + + children: (Diddy & { funky?: string })[]; +>children : Symbol(children, Decl(recursiveExcessPropertyChecks.ts, 30, 22)) +>Diddy : Symbol(Diddy, Decl(recursiveExcessPropertyChecks.ts, 18, 1)) +>funky : Symbol(funky, Decl(recursiveExcessPropertyChecks.ts, 31, 24)) + +}; +type X = Dandy["children"] +>X : Symbol(X, Decl(recursiveExcessPropertyChecks.ts, 32, 2)) +>Dandy : Symbol(Dandy, Decl(recursiveExcessPropertyChecks.ts, 28, 1)) + +var x: X +>x : Symbol(x, Decl(recursiveExcessPropertyChecks.ts, 34, 3)) +>X : Symbol(X, Decl(recursiveExcessPropertyChecks.ts, 32, 2)) + +const mainView: Dandy = { +>mainView : Symbol(mainView, Decl(recursiveExcessPropertyChecks.ts, 36, 5)) +>Dandy : Symbol(Dandy, Decl(recursiveExcessPropertyChecks.ts, 28, 1)) + + diddy: "", +>diddy : Symbol(diddy, Decl(recursiveExcessPropertyChecks.ts, 36, 25)) + + children: [ +>children : Symbol(children, Decl(recursiveExcessPropertyChecks.ts, 37, 14)) + { + diddy: "", +>diddy : Symbol(diddy, Decl(recursiveExcessPropertyChecks.ts, 39, 9)) + + funky: "Empty" // <- Incorrect error +>funky : Symbol(funky, Decl(recursiveExcessPropertyChecks.ts, 40, 22)) + } + ], +}; +// Legal +const p = mainView.children[0].funky +>p : Symbol(p, Decl(recursiveExcessPropertyChecks.ts, 46, 5)) +>mainView.children[0].funky : Symbol(funky, Decl(recursiveExcessPropertyChecks.ts, 31, 24)) +>mainView.children : Symbol(children, Decl(recursiveExcessPropertyChecks.ts, 21, 18), Decl(recursiveExcessPropertyChecks.ts, 30, 22)) +>mainView : Symbol(mainView, Decl(recursiveExcessPropertyChecks.ts, 36, 5)) +>children : Symbol(children, Decl(recursiveExcessPropertyChecks.ts, 21, 18), Decl(recursiveExcessPropertyChecks.ts, 30, 22)) +>funky : Symbol(funky, Decl(recursiveExcessPropertyChecks.ts, 31, 24)) + diff --git a/tests/baselines/reference/recursiveExcessPropertyChecks.types b/tests/baselines/reference/recursiveExcessPropertyChecks.types index 04b78367460b5..c95d07924360c 100644 --- a/tests/baselines/reference/recursiveExcessPropertyChecks.types +++ b/tests/baselines/reference/recursiveExcessPropertyChecks.types @@ -25,3 +25,75 @@ getMaxId(nodes); >getMaxId : (items: NodeWithId[]) => void >nodes : ITreeItem[] + +// Repro from #42715 +export interface Donkey { + donkey: string; +>donkey : string +} + +interface Diddy { + diddy: string; +>diddy : string + + children?: Diddy[] | Donkey; +>children : Donkey | Diddy[] | undefined +} + +interface Cranky { + cranky: string; +>cranky : string + + children: Diddy; +>children : Diddy +} + +type Dandy = Diddy & { +>Dandy : Dandy + + children: (Diddy & { funky?: string })[]; +>children : (Diddy & { funky?: string | undefined; })[] +>funky : string | undefined + +}; +type X = Dandy["children"] +>X : (Donkey | Diddy[] | undefined) & (Diddy & { funky?: string | undefined; })[] + +var x: X +>x : (Donkey | Diddy[] | undefined) & (Diddy & { funky?: string | undefined; })[] + +const mainView: Dandy = { +>mainView : Dandy +>{ diddy: "", children: [ { diddy: "", funky: "Empty" // <- Incorrect error } ],} : { diddy: string; children: { diddy: string; funky: string; }[]; } + + diddy: "", +>diddy : string +>"" : "" + + children: [ +>children : { diddy: string; funky: string; }[] +>[ { diddy: "", funky: "Empty" // <- Incorrect error } ] : { diddy: string; funky: string; }[] + { +>{ diddy: "", funky: "Empty" // <- Incorrect error } : { diddy: string; funky: string; } + + diddy: "", +>diddy : string +>"" : "" + + funky: "Empty" // <- Incorrect error +>funky : string +>"Empty" : "Empty" + } + ], +}; +// Legal +const p = mainView.children[0].funky +>p : string | undefined +>mainView.children[0].funky : string | undefined +>mainView.children[0] : Diddy & { funky?: string | undefined; } +>mainView.children : (Donkey | Diddy[] | undefined) & (Diddy & { funky?: string | undefined; })[] +>mainView : Dandy +>children : (Donkey | Diddy[] | undefined) & (Diddy & { funky?: string | undefined; })[] +>0 : 0 +>funky : string | undefined + diff --git a/tests/cases/compiler/recursiveExcessPropertyChecks.ts b/tests/cases/compiler/recursiveExcessPropertyChecks.ts index 210f6fa6a9f77..ecf55cee6bb7f 100644 --- a/tests/cases/compiler/recursiveExcessPropertyChecks.ts +++ b/tests/cases/compiler/recursiveExcessPropertyChecks.ts @@ -13,3 +13,37 @@ function getMaxId(items: NodeWithId[]) { const nodes = [] as ITreeItem[]; getMaxId(nodes); + + +// Repro from #42715 +export interface Donkey { + donkey: string; +} + +interface Diddy { + diddy: string; + children?: Diddy[] | Donkey; +} + +interface Cranky { + cranky: string; + children: Diddy; +} + +type Dandy = Diddy & { + children: (Diddy & { funky?: string })[]; +}; +type X = Dandy["children"] +var x: X + +const mainView: Dandy = { + diddy: "", + children: [ + { + diddy: "", + funky: "Empty" // <- Incorrect error + } + ], +}; +// Legal +const p = mainView.children[0].funky From 04d48ecf133c787dd5100aa20f5224062af1ed00 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 16 Apr 2021 10:15:07 -0700 Subject: [PATCH 2/2] fix parameter name lint --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index fa7a101928c10..d04584dec6087 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17883,7 +17883,7 @@ namespace ts { } } for (const type of targetTypes) { - const related = isRelatedTo(source, type, /*reportErrors*/ false, undefined, intersectionState); + const related = isRelatedTo(source, type, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState); if (related) { return related; }