From c6935b4d77830626d9a5b831cd226bfef19f663e Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Sat, 5 Oct 2019 16:22:59 +0100 Subject: [PATCH 1/2] Fix #33732 --- src/compiler/checker.ts | 5 +- .../excessPropertyCheckWithUnions.errors.txt | 40 +++++++++ .../excessPropertyCheckWithUnions.js | 52 ++++++++++++ .../excessPropertyCheckWithUnions.symbols | 84 +++++++++++++++++++ .../excessPropertyCheckWithUnions.types | 75 +++++++++++++++++ .../compiler/excessPropertyCheckWithUnions.ts | 40 +++++++++ 6 files changed, 292 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c2603e1f30b0b..85f12ddfcefa2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15582,10 +15582,7 @@ namespace ts { } } } - if (unionParent && !result && targetIsOptional) { - result = isRelatedTo(source, undefinedType); - } - if (unionParent && !result && reportErrors) { + if (unionParent && !result) { // The easiest way to get the right errors here is to un-defer (which may be costly) // If it turns out this is too costly too often, we can replicate the error handling logic within // typeRelatedToSomeType without the discriminatable type branch (as that requires a manifest union diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt index 72da324caf759..ab8a703f9a130 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -155,4 +155,44 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(87,5): error TS2322: Type !!! error TS2322: Type '{ tag: "button"; type: "submit"; href: string; }' is not assignable to type 'Union'. !!! error TS2322: Object literal may only specify known properties, and 'href' does not exist in type 'Button'. }; + + + // Repro from #33732 + + interface I1 { + prop1: string; + } + + interface I2 { + prop2: string; + } + + interface I3 extends Record { + + } + + type Properties = + | { [key: string]: never } + | I1 + | I2 + | I3 + ; + + + declare const prop1: string; + declare const prop2: string | undefined; + + function F1(_arg: { props: Properties }) { } + F1({ + props: { + prop1, + prop2, + }, + }); + + function F2(_props: Properties) { } + F2({ + prop1, + prop2, + }); \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.js b/tests/baselines/reference/excessPropertyCheckWithUnions.js index 691a64f65b808..0ed01611989ea 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.js +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.js @@ -87,6 +87,46 @@ const obj: Union = { // should have error here href: 'foo', }; + + +// Repro from #33732 + +interface I1 { + prop1: string; +} + +interface I2 { + prop2: string; +} + +interface I3 extends Record { + +} + +type Properties = + | { [key: string]: never } + | I1 + | I2 + | I3 + ; + + +declare const prop1: string; +declare const prop2: string | undefined; + +function F1(_arg: { props: Properties }) { } +F1({ + props: { + prop1, + prop2, + }, +}); + +function F2(_props: Properties) { } +F2({ + prop1, + prop2, +}); //// [excessPropertyCheckWithUnions.js] @@ -144,3 +184,15 @@ var obj = { // should have error here href: 'foo' }; +function F1(_arg) { } +F1({ + props: { + prop1: prop1, + prop2: prop2 + } +}); +function F2(_props) { } +F2({ + prop1: prop1, + prop2: prop2 +}); diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.symbols b/tests/baselines/reference/excessPropertyCheckWithUnions.symbols index 749e359d4a1cc..444aab1f07c71 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.symbols +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.symbols @@ -254,3 +254,87 @@ const obj: Union = { }; + +// Repro from #33732 + +interface I1 { +>I1 : Symbol(I1, Decl(excessPropertyCheckWithUnions.ts, 87, 2)) + + prop1: string; +>prop1 : Symbol(I1.prop1, Decl(excessPropertyCheckWithUnions.ts, 92, 14)) +} + +interface I2 { +>I2 : Symbol(I2, Decl(excessPropertyCheckWithUnions.ts, 94, 1)) + + prop2: string; +>prop2 : Symbol(I2.prop2, Decl(excessPropertyCheckWithUnions.ts, 96, 14)) +} + +interface I3 extends Record { +>I3 : Symbol(I3, Decl(excessPropertyCheckWithUnions.ts, 98, 1)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) + +} + +type Properties = +>Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 102, 1)) + + | { [key: string]: never } +>key : Symbol(key, Decl(excessPropertyCheckWithUnions.ts, 105, 9)) + + | I1 +>I1 : Symbol(I1, Decl(excessPropertyCheckWithUnions.ts, 87, 2)) + + | I2 +>I2 : Symbol(I2, Decl(excessPropertyCheckWithUnions.ts, 94, 1)) + + | I3 +>I3 : Symbol(I3, Decl(excessPropertyCheckWithUnions.ts, 98, 1)) + + ; + + +declare const prop1: string; +>prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 112, 13)) + +declare const prop2: string | undefined; +>prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 113, 13)) + +function F1(_arg: { props: Properties }) { } +>F1 : Symbol(F1, Decl(excessPropertyCheckWithUnions.ts, 113, 40)) +>_arg : Symbol(_arg, Decl(excessPropertyCheckWithUnions.ts, 115, 12)) +>props : Symbol(props, Decl(excessPropertyCheckWithUnions.ts, 115, 19)) +>Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 102, 1)) + +F1({ +>F1 : Symbol(F1, Decl(excessPropertyCheckWithUnions.ts, 113, 40)) + + props: { +>props : Symbol(props, Decl(excessPropertyCheckWithUnions.ts, 116, 4)) + + prop1, +>prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 117, 12)) + + prop2, +>prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 118, 14)) + + }, +}); + +function F2(_props: Properties) { } +>F2 : Symbol(F2, Decl(excessPropertyCheckWithUnions.ts, 121, 3)) +>_props : Symbol(_props, Decl(excessPropertyCheckWithUnions.ts, 123, 12)) +>Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 102, 1)) + +F2({ +>F2 : Symbol(F2, Decl(excessPropertyCheckWithUnions.ts, 121, 3)) + + prop1, +>prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 124, 4)) + + prop2, +>prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 125, 10)) + +}); + diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.types b/tests/baselines/reference/excessPropertyCheckWithUnions.types index f56aa287daa7e..4a195146ffff3 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.types +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.types @@ -312,3 +312,78 @@ const obj: Union = { }; + +// Repro from #33732 + +interface I1 { + prop1: string; +>prop1 : string +} + +interface I2 { + prop2: string; +>prop2 : string +} + +interface I3 extends Record { + +} + +type Properties = +>Properties : Properties + + | { [key: string]: never } +>key : string + + | I1 + | I2 + | I3 + ; + + +declare const prop1: string; +>prop1 : string + +declare const prop2: string | undefined; +>prop2 : string | undefined + +function F1(_arg: { props: Properties }) { } +>F1 : (_arg: { props: Properties; }) => void +>_arg : { props: Properties; } +>props : Properties + +F1({ +>F1({ props: { prop1, prop2, },}) : void +>F1 : (_arg: { props: Properties; }) => void +>{ props: { prop1, prop2, },} : { props: { prop1: string; prop2: string | undefined; }; } + + props: { +>props : { prop1: string; prop2: string | undefined; } +>{ prop1, prop2, } : { prop1: string; prop2: string | undefined; } + + prop1, +>prop1 : string + + prop2, +>prop2 : string | undefined + + }, +}); + +function F2(_props: Properties) { } +>F2 : (_props: Properties) => void +>_props : Properties + +F2({ +>F2({ prop1, prop2,}) : void +>F2 : (_props: Properties) => void +>{ prop1, prop2,} : { prop1: string; prop2: string | undefined; } + + prop1, +>prop1 : string + + prop2, +>prop2 : string | undefined + +}); + diff --git a/tests/cases/compiler/excessPropertyCheckWithUnions.ts b/tests/cases/compiler/excessPropertyCheckWithUnions.ts index bb3e59151fc2a..fa76aba4bd901 100644 --- a/tests/cases/compiler/excessPropertyCheckWithUnions.ts +++ b/tests/cases/compiler/excessPropertyCheckWithUnions.ts @@ -87,3 +87,43 @@ const obj: Union = { // should have error here href: 'foo', }; + + +// Repro from #33732 + +interface I1 { + prop1: string; +} + +interface I2 { + prop2: string; +} + +interface I3 extends Record { + +} + +type Properties = + | { [key: string]: never } + | I1 + | I2 + | I3 + ; + + +declare const prop1: string; +declare const prop2: string | undefined; + +function F1(_arg: { props: Properties }) { } +F1({ + props: { + prop1, + prop2, + }, +}); + +function F2(_props: Properties) { } +F2({ + prop1, + prop2, +}); From c691df968c5328db403ee7b0ddc203d000692438 Mon Sep 17 00:00:00 2001 From: Jack Williams Date: Wed, 8 Jan 2020 16:30:55 +0000 Subject: [PATCH 2/2] Simplify fix --- src/compiler/checker.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c06e94bbcb22e..86d1021145897 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15863,7 +15863,10 @@ namespace ts { } } } - if (unionParent && !result) { + if (unionParent && !result && targetIsOptional) { + result = isRelatedTo(source, undefinedType); + } + if (unionParent && !result && source.flags & TypeFlags.Union) { // The easiest way to get the right errors here is to un-defer (which may be costly) // If it turns out this is too costly too often, we can replicate the error handling logic within // typeRelatedToSomeType without the discriminatable type branch (as that requires a manifest union