From ca945a254a7687226ebabd662a50ce7f56d0805e Mon Sep 17 00:00:00 2001 From: Nebojsa Cvetkovic Date: Tue, 13 Dec 2022 03:58:35 +0000 Subject: [PATCH] Consider all union types matching discriminator for excess property checks Fixes #51873 --- src/compiler/checker.ts | 10 +- ...yCheckWithMultipleDiscriminants.errors.txt | 54 +++- ...sPropertyCheckWithMultipleDiscriminants.js | 53 +++- ...ertyCheckWithMultipleDiscriminants.symbols | 82 +++++- ...opertyCheckWithMultipleDiscriminants.types | 76 ++++- .../excessPropertyCheckWithUnions.errors.txt | 55 ++-- .../excessPropertyCheckWithUnions.js | 10 +- .../excessPropertyCheckWithUnions.symbols | 260 +++++++++--------- .../excessPropertyCheckWithUnions.types | 12 +- .../objectLiteralNormalization.errors.txt | 6 +- ...sPropertyCheckWithMultipleDiscriminants.ts | 37 ++- .../compiler/excessPropertyCheckWithUnions.ts | 6 +- 12 files changed, 463 insertions(+), 198 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7a9572c993c51..8ed96e399cd28 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22061,15 +22061,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (match === -1) { return defaultValue; } - // make sure exactly 1 matches before returning it - let nextMatch = discriminable.indexOf(/*searchElement*/ true, match + 1); - while (nextMatch !== -1) { - if (!isTypeIdenticalTo(target.types[match], target.types[nextMatch])) { - return defaultValue; - } - nextMatch = discriminable.indexOf(/*searchElement*/ true, nextMatch + 1); - } - return target.types[match]; + return getUnionType(target.types.filter((_, index) => discriminable[index])); } /** diff --git a/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.errors.txt b/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.errors.txt index 29575580e8087..3b5ac675bab34 100644 --- a/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.errors.txt @@ -2,11 +2,17 @@ tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts(30,5): erro Object literal may only specify known properties, and 'multipleOf' does not exist in type 'Float'. tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts(41,5): error TS2322: Type '{ p1: "left"; p2: false; p3: number; p4: string; }' is not assignable to type 'DisjointDiscriminants'. Object literal may only specify known properties, and 'p3' does not exist in type '{ p1: "left"; p2: boolean; }'. +tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts(50,5): error TS2322: Type '{ p1: "left"; p2: true; p3: number; p4: string; }' is not assignable to type 'DisjointDiscriminants'. + Object literal may only specify known properties, and 'p4' does not exist in type '{ p1: "left"; p2: true; p3: number; } | { p1: "left"; p2: boolean; }'. tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts(57,5): error TS2322: Type '{ p1: "right"; p2: false; p3: number; p4: string; }' is not assignable to type 'DisjointDiscriminants'. Object literal may only specify known properties, and 'p3' does not exist in type '{ p1: "right"; p2: false; p4: string; }'. +tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts(83,5): error TS2322: Type '{ type: "A"; n: number; a: number; b: number; }' is not assignable to type 'CommonWithOverlappingOptionals'. + Object literal may only specify known properties, and 'b' does not exist in type 'Common | (Common & A)'. +tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts(93,5): error TS2322: Type '{ type: "A"; n: number; a: number; b: number; }' is not assignable to type 'CommonWithDisjointOverlappingOptionals'. + Object literal may only specify known properties, and 'b' does not exist in type 'Common | A'. -==== tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts (3 errors) ==== +==== tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts (6 errors) ==== // Repro from #32657 interface Base { @@ -57,12 +63,15 @@ tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts(57,5): erro p4: "hello" }; - // This has no excess error because variant one and three are both applicable. + // This has excess error because variant two is not applicable. const b: DisjointDiscriminants = { p1: 'left', p2: true, p3: 42, p4: "hello" + ~~~~~~~~~~~ +!!! error TS2322: Type '{ p1: "left"; p2: true; p3: number; p4: string; }' is not assignable to type 'DisjointDiscriminants'. +!!! error TS2322: Object literal may only specify known properties, and 'p4' does not exist in type '{ p1: "left"; p2: true; p3: number; } | { p1: "left"; p2: boolean; }'. }; // This has excess error because variant two is the only applicable case @@ -75,4 +84,45 @@ tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts(57,5): erro !!! error TS2322: Object literal may only specify known properties, and 'p3' does not exist in type '{ p1: "right"; p2: false; p4: string; }'. p4: "hello" }; + + // Repro from #51873 + + interface Common { + type: "A" | "B" | "C" | "D"; + n: number; + } + interface A { + type: "A"; + a?: number; + } + interface B { + type: "B"; + b?: number; + } + + type CommonWithOverlappingOptionals = Common | (Common & A) | (Common & B); + + // Should reject { b } because reduced to Common | (Common & A) + const c1: CommonWithOverlappingOptionals = { + type: "A", + n: 1, + a: 1, + b: 1 // excess property + ~~~~ +!!! error TS2322: Type '{ type: "A"; n: number; a: number; b: number; }' is not assignable to type 'CommonWithOverlappingOptionals'. +!!! error TS2322: Object literal may only specify known properties, and 'b' does not exist in type 'Common | (Common & A)'. + } + + type CommonWithDisjointOverlappingOptionals = Common | A | B; + + // Should still reject { b } because reduced to Common | A, even though these are now disjoint + const c2: CommonWithDisjointOverlappingOptionals = { + type: "A", + n: 1, + a: 1, + b: 1 // excess property + ~~~~ +!!! error TS2322: Type '{ type: "A"; n: number; a: number; b: number; }' is not assignable to type 'CommonWithDisjointOverlappingOptionals'. +!!! error TS2322: Object literal may only specify known properties, and 'b' does not exist in type 'Common | A'. + } \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.js b/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.js index a847e63aa5a1f..6f930bbc9ef29 100644 --- a/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.js +++ b/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.js @@ -43,7 +43,7 @@ const a: DisjointDiscriminants = { p4: "hello" }; -// This has no excess error because variant one and three are both applicable. +// This has excess error because variant two is not applicable. const b: DisjointDiscriminants = { p1: 'left', p2: true, @@ -58,6 +58,41 @@ const c: DisjointDiscriminants = { p3: 42, p4: "hello" }; + +// Repro from #51873 + +interface Common { + type: "A" | "B" | "C" | "D"; + n: number; +} +interface A { + type: "A"; + a?: number; +} +interface B { + type: "B"; + b?: number; +} + +type CommonWithOverlappingOptionals = Common | (Common & A) | (Common & B); + +// Should reject { b } because reduced to Common | (Common & A) +const c1: CommonWithOverlappingOptionals = { + type: "A", + n: 1, + a: 1, + b: 1 // excess property +} + +type CommonWithDisjointOverlappingOptionals = Common | A | B; + +// Should still reject { b } because reduced to Common | A, even though these are now disjoint +const c2: CommonWithDisjointOverlappingOptionals = { + type: "A", + n: 1, + a: 1, + b: 1 // excess property +} //// [excessPropertyCheckWithMultipleDiscriminants.js] @@ -75,7 +110,7 @@ var a = { p3: 42, p4: "hello" }; -// This has no excess error because variant one and three are both applicable. +// This has excess error because variant two is not applicable. var b = { p1: 'left', p2: true, @@ -89,3 +124,17 @@ var c = { p3: 42, p4: "hello" }; +// Should reject { b } because reduced to Common | (Common & A) +var c1 = { + type: "A", + n: 1, + a: 1, + b: 1 // excess property +}; +// Should still reject { b } because reduced to Common | A, even though these are now disjoint +var c2 = { + type: "A", + n: 1, + a: 1, + b: 1 // excess property +}; diff --git a/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.symbols b/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.symbols index 7a1b06b045a74..090932206afca 100644 --- a/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.symbols +++ b/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.symbols @@ -103,7 +103,7 @@ const a: DisjointDiscriminants = { }; -// This has no excess error because variant one and three are both applicable. +// This has excess error because variant two is not applicable. const b: DisjointDiscriminants = { >b : Symbol(b, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 45, 5)) >DisjointDiscriminants : Symbol(DisjointDiscriminants, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 31, 1)) @@ -141,3 +141,83 @@ const c: DisjointDiscriminants = { }; +// Repro from #51873 + +interface Common { +>Common : Symbol(Common, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 58, 2)) + + type: "A" | "B" | "C" | "D"; +>type : Symbol(Common.type, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 62, 18)) + + n: number; +>n : Symbol(Common.n, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 63, 32)) +} +interface A { +>A : Symbol(A, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 65, 1)) + + type: "A"; +>type : Symbol(A.type, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 66, 13)) + + a?: number; +>a : Symbol(A.a, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 67, 14)) +} +interface B { +>B : Symbol(B, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 69, 1)) + + type: "B"; +>type : Symbol(B.type, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 70, 13)) + + b?: number; +>b : Symbol(B.b, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 71, 14)) +} + +type CommonWithOverlappingOptionals = Common | (Common & A) | (Common & B); +>CommonWithOverlappingOptionals : Symbol(CommonWithOverlappingOptionals, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 73, 1)) +>Common : Symbol(Common, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 58, 2)) +>Common : Symbol(Common, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 58, 2)) +>A : Symbol(A, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 65, 1)) +>Common : Symbol(Common, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 58, 2)) +>B : Symbol(B, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 69, 1)) + +// Should reject { b } because reduced to Common | (Common & A) +const c1: CommonWithOverlappingOptionals = { +>c1 : Symbol(c1, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 78, 5)) +>CommonWithOverlappingOptionals : Symbol(CommonWithOverlappingOptionals, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 73, 1)) + + type: "A", +>type : Symbol(type, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 78, 44)) + + n: 1, +>n : Symbol(n, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 79, 14)) + + a: 1, +>a : Symbol(a, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 80, 9)) + + b: 1 // excess property +>b : Symbol(b, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 81, 9)) +} + +type CommonWithDisjointOverlappingOptionals = Common | A | B; +>CommonWithDisjointOverlappingOptionals : Symbol(CommonWithDisjointOverlappingOptionals, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 83, 1)) +>Common : Symbol(Common, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 58, 2)) +>A : Symbol(A, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 65, 1)) +>B : Symbol(B, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 69, 1)) + +// Should still reject { b } because reduced to Common | A, even though these are now disjoint +const c2: CommonWithDisjointOverlappingOptionals = { +>c2 : Symbol(c2, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 88, 5)) +>CommonWithDisjointOverlappingOptionals : Symbol(CommonWithDisjointOverlappingOptionals, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 83, 1)) + + type: "A", +>type : Symbol(type, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 88, 52)) + + n: 1, +>n : Symbol(n, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 89, 14)) + + a: 1, +>a : Symbol(a, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 90, 9)) + + b: 1 // excess property +>b : Symbol(b, Decl(excessPropertyCheckWithMultipleDiscriminants.ts, 91, 9)) +} + diff --git a/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.types b/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.types index 2c5b07e591444..79dd2fce363a6 100644 --- a/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.types +++ b/tests/baselines/reference/excessPropertyCheckWithMultipleDiscriminants.types @@ -93,7 +93,7 @@ const a: DisjointDiscriminants = { }; -// This has no excess error because variant one and three are both applicable. +// This has excess error because variant two is not applicable. const b: DisjointDiscriminants = { >b : DisjointDiscriminants >{ p1: 'left', p2: true, p3: 42, p4: "hello"} : { p1: "left"; p2: true; p3: number; p4: string; } @@ -139,3 +139,77 @@ const c: DisjointDiscriminants = { }; +// Repro from #51873 + +interface Common { + type: "A" | "B" | "C" | "D"; +>type : "A" | "B" | "C" | "D" + + n: number; +>n : number +} +interface A { + type: "A"; +>type : "A" + + a?: number; +>a : number +} +interface B { + type: "B"; +>type : "B" + + b?: number; +>b : number +} + +type CommonWithOverlappingOptionals = Common | (Common & A) | (Common & B); +>CommonWithOverlappingOptionals : Common | (Common & A) | (Common & B) + +// Should reject { b } because reduced to Common | (Common & A) +const c1: CommonWithOverlappingOptionals = { +>c1 : CommonWithOverlappingOptionals +>{ type: "A", n: 1, a: 1, b: 1 // excess property} : { type: "A"; n: number; a: number; b: number; } + + type: "A", +>type : "A" +>"A" : "A" + + n: 1, +>n : number +>1 : 1 + + a: 1, +>a : number +>1 : 1 + + b: 1 // excess property +>b : number +>1 : 1 +} + +type CommonWithDisjointOverlappingOptionals = Common | A | B; +>CommonWithDisjointOverlappingOptionals : Common | A | B + +// Should still reject { b } because reduced to Common | A, even though these are now disjoint +const c2: CommonWithDisjointOverlappingOptionals = { +>c2 : CommonWithDisjointOverlappingOptionals +>{ type: "A", n: 1, a: 1, b: 1 // excess property} : { type: "A"; n: number; a: number; b: number; } + + type: "A", +>type : "A" +>"A" : "A" + + n: 1, +>n : number +>1 : 1 + + a: 1, +>a : number +>1 : 1 + + b: 1 // excess property +>b : number +>1 : 1 +} + diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt index 8c34a0f5d69cf..fb33e81d25664 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -5,30 +5,27 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(11,21): error TS2322: Type tests/cases/compiler/excessPropertyCheckWithUnions.ts(12,1): error TS2322: Type '{ tag: "D"; }' is not assignable to type 'ADT'. Property 'd20' is missing in type '{ tag: "D"; }' but required in type '{ tag: "D"; d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; }'. tests/cases/compiler/excessPropertyCheckWithUnions.ts(33,28): error TS2322: Type '{ tag: "A"; x: string; extra: number; }' is not assignable to type 'Ambiguous'. - Object literal may only specify known properties, and 'extra' does not exist in type 'Ambiguous'. + Object literal may only specify known properties, and 'extra' does not exist in type '{ tag: "A"; x: string; } | { tag: "A"; y: number; }'. tests/cases/compiler/excessPropertyCheckWithUnions.ts(34,26): error TS2322: Type '{ tag: "A"; y: number; extra: number; }' is not assignable to type 'Ambiguous'. - Object literal may only specify known properties, and 'extra' does not exist in type 'Ambiguous'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(39,1): error TS2322: Type '{ tag: "A"; }' is not assignable to type 'Ambiguous'. - Type '{ tag: "A"; }' is not assignable to type '{ tag: "C"; }'. - Types of property 'tag' are incompatible. - Type '"A"' is not assignable to type '"C"'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(40,1): error TS2322: Type '{ tag: "A"; z: true; }' is not assignable to type 'Ambiguous'. - Type '{ tag: "A"; z: true; }' is not assignable to type '{ tag: "B"; z: boolean; }'. - Types of property 'tag' are incompatible. - Type '"A"' is not assignable to type '"B"'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(49,35): error TS2322: Type '{ a: 1; b: 1; first: string; second: string; }' is not assignable to type 'Overlapping'. + Object literal may only specify known properties, and 'extra' does not exist in type '{ tag: "A"; x: string; } | { tag: "A"; y: number; }'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(37,1): error TS2322: Type '{ tag: "A"; }' is not assignable to type 'Ambiguous'. + Type '{ tag: "A"; }' is not assignable to type '{ tag: "A"; x: string; } | { tag: "A"; y: number; }'. + Property 'y' is missing in type '{ tag: "A"; }' but required in type '{ tag: "A"; y: number; }'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(38,19): error TS2322: Type '{ tag: "A"; z: boolean; }' is not assignable to type 'Ambiguous'. + Object literal may only specify known properties, and 'z' does not exist in type '{ tag: "A"; x: string; } | { tag: "A"; y: number; }'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(47,35): error TS2322: Type '{ a: 1; b: 1; first: string; second: string; }' is not assignable to type 'Overlapping'. Object literal may only specify known properties, and 'second' does not exist in type '{ a: 1; b: 1; first: string; }'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(50,35): error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(48,35): error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'. Object literal may only specify known properties, and 'third' does not exist in type '{ a: 1; b: 1; first: string; }'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(64,9): error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'. Types of property 'n' are incompatible. Type '{ a: string; b: string; }' is not assignable to type 'AN'. Object literal may only specify known properties, and 'b' does not exist in type 'AN'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(87,5): error TS2322: Type '{ tag: "button"; type: "submit"; href: string; }' is not assignable to type 'Union'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(85,5): error TS2322: Type '{ tag: "button"; type: "submit"; href: string; }' is not assignable to type 'Union'. Object literal may only specify known properties, and 'href' does not exist in type 'Button'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(108,5): error TS2322: Type 'string' is not assignable to type 'IValue'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(113,67): error TS2322: Type 'string' is not assignable to type 'number'. -tests/cases/compiler/excessPropertyCheckWithUnions.ts(114,63): error TS2322: Type 'string' is not assignable to type 'number'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(106,5): error TS2322: Type 'string' is not assignable to type 'IValue'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(111,67): error TS2322: Type 'string' is not assignable to type 'number'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(112,63): error TS2322: Type 'string' is not assignable to type 'number'. ==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (14 errors) ==== @@ -77,27 +74,23 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(114,63): error TS2322: Typ amb = { tag: "A", x: "hi", extra: 12 } ~~~~~~~~~ !!! error TS2322: Type '{ tag: "A"; x: string; extra: number; }' is not assignable to type 'Ambiguous'. -!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type 'Ambiguous'. +!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type '{ tag: "A"; x: string; } | { tag: "A"; y: number; }'. amb = { tag: "A", y: 12, extra: 12 } ~~~~~~~~~ !!! error TS2322: Type '{ tag: "A"; y: number; extra: number; }' is not assignable to type 'Ambiguous'. -!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type 'Ambiguous'. +!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type '{ tag: "A"; x: string; } | { tag: "A"; y: number; }'. - // assignability errors still work. - // But note that the error for `z: true` is the fallback one of reporting on - // the last constituent since assignability error reporting can't find a single best discriminant either. + // assignability errors still work amb = { tag: "A" } ~~~ !!! error TS2322: Type '{ tag: "A"; }' is not assignable to type 'Ambiguous'. -!!! error TS2322: Type '{ tag: "A"; }' is not assignable to type '{ tag: "C"; }'. -!!! error TS2322: Types of property 'tag' are incompatible. -!!! error TS2322: Type '"A"' is not assignable to type '"C"'. +!!! error TS2322: Type '{ tag: "A"; }' is not assignable to type '{ tag: "A"; x: string; } | { tag: "A"; y: number; }'. +!!! error TS2322: Property 'y' is missing in type '{ tag: "A"; }' but required in type '{ tag: "A"; y: number; }'. +!!! related TS2728 tests/cases/compiler/excessPropertyCheckWithUnions.ts:19:5: 'y' is declared here. amb = { tag: "A", z: true } - ~~~ -!!! error TS2322: Type '{ tag: "A"; z: true; }' is not assignable to type 'Ambiguous'. -!!! error TS2322: Type '{ tag: "A"; z: true; }' is not assignable to type '{ tag: "B"; z: boolean; }'. -!!! error TS2322: Types of property 'tag' are incompatible. -!!! error TS2322: Type '"A"' is not assignable to type '"B"'. + ~~~~~~~ +!!! error TS2322: Type '{ tag: "A"; z: boolean; }' is not assignable to type 'Ambiguous'. +!!! error TS2322: Object literal may only specify known properties, and 'z' does not exist in type '{ tag: "A"; x: string; } | { tag: "A"; y: number; }'. type Overlapping = | { a: 1, b: 1, first: string } @@ -167,7 +160,7 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(114,63): error TS2322: Typ value: string } - interface StringKeys { + interface StringKeys { [propertyName: string]: IValue; }; diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.js b/tests/baselines/reference/excessPropertyCheckWithUnions.js index dabcd824f3693..40be4c89c175e 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.js +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.js @@ -34,9 +34,7 @@ amb = { tag: "A", x: "hi", y: 12 } amb = { tag: "A", x: "hi", extra: 12 } amb = { tag: "A", y: 12, extra: 12 } -// assignability errors still work. -// But note that the error for `z: true` is the fallback one of reporting on -// the last constituent since assignability error reporting can't find a single best discriminant either. +// assignability errors still work amb = { tag: "A" } amb = { tag: "A", z: true } @@ -94,7 +92,7 @@ interface IValue { value: string } -interface StringKeys { +interface StringKeys { [propertyName: string]: IValue; }; @@ -178,9 +176,7 @@ amb = { tag: "A", x: "hi", y: 12 }; // correctly error on excess property 'extra', even when ambiguous amb = { tag: "A", x: "hi", extra: 12 }; amb = { tag: "A", y: 12, extra: 12 }; -// assignability errors still work. -// But note that the error for `z: true` is the fallback one of reporting on -// the last constituent since assignability error reporting can't find a single best discriminant either. +// assignability errors still work amb = { tag: "A" }; amb = { tag: "A", z: true }; var over; diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.symbols b/tests/baselines/reference/excessPropertyCheckWithUnions.symbols index 80921fedad26c..65cec78a47b5a 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.symbols +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.symbols @@ -95,301 +95,299 @@ amb = { tag: "A", y: 12, extra: 12 } >y : Symbol(y, Decl(excessPropertyCheckWithUnions.ts, 33, 17)) >extra : Symbol(extra, Decl(excessPropertyCheckWithUnions.ts, 33, 24)) -// assignability errors still work. -// But note that the error for `z: true` is the fallback one of reporting on -// the last constituent since assignability error reporting can't find a single best discriminant either. +// assignability errors still work amb = { tag: "A" } >amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) ->tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 38, 7)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 36, 7)) amb = { tag: "A", z: true } >amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) ->tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 39, 7)) ->z : Symbol(z, Decl(excessPropertyCheckWithUnions.ts, 39, 17)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 37, 7)) +>z : Symbol(z, Decl(excessPropertyCheckWithUnions.ts, 37, 17)) type Overlapping = ->Overlapping : Symbol(Overlapping, Decl(excessPropertyCheckWithUnions.ts, 39, 27)) +>Overlapping : Symbol(Overlapping, Decl(excessPropertyCheckWithUnions.ts, 37, 27)) | { a: 1, b: 1, first: string } ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 42, 7)) ->b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 42, 13)) ->first : Symbol(first, Decl(excessPropertyCheckWithUnions.ts, 42, 19)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 40, 7)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 40, 13)) +>first : Symbol(first, Decl(excessPropertyCheckWithUnions.ts, 40, 19)) | { a: 2, second: string } ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 43, 7)) ->second : Symbol(second, Decl(excessPropertyCheckWithUnions.ts, 43, 13)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 41, 7)) +>second : Symbol(second, Decl(excessPropertyCheckWithUnions.ts, 41, 13)) | { b: 3, third: string } ->b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 44, 7)) ->third : Symbol(third, Decl(excessPropertyCheckWithUnions.ts, 44, 13)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 42, 7)) +>third : Symbol(third, Decl(excessPropertyCheckWithUnions.ts, 42, 13)) let over: Overlapping ->over : Symbol(over, Decl(excessPropertyCheckWithUnions.ts, 45, 3)) ->Overlapping : Symbol(Overlapping, Decl(excessPropertyCheckWithUnions.ts, 39, 27)) +>over : Symbol(over, Decl(excessPropertyCheckWithUnions.ts, 43, 3)) +>Overlapping : Symbol(Overlapping, Decl(excessPropertyCheckWithUnions.ts, 37, 27)) // these two are still errors despite their doubled up discriminants over = { a: 1, b: 1, first: "ok", second: "error" } ->over : Symbol(over, Decl(excessPropertyCheckWithUnions.ts, 45, 3)) ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 48, 8)) ->b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 48, 14)) ->first : Symbol(first, Decl(excessPropertyCheckWithUnions.ts, 48, 20)) ->second : Symbol(second, Decl(excessPropertyCheckWithUnions.ts, 48, 33)) +>over : Symbol(over, Decl(excessPropertyCheckWithUnions.ts, 43, 3)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 46, 8)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 46, 14)) +>first : Symbol(first, Decl(excessPropertyCheckWithUnions.ts, 46, 20)) +>second : Symbol(second, Decl(excessPropertyCheckWithUnions.ts, 46, 33)) over = { a: 1, b: 1, first: "ok", third: "error" } ->over : Symbol(over, Decl(excessPropertyCheckWithUnions.ts, 45, 3)) ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 49, 8)) ->b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 49, 14)) ->first : Symbol(first, Decl(excessPropertyCheckWithUnions.ts, 49, 20)) ->third : Symbol(third, Decl(excessPropertyCheckWithUnions.ts, 49, 33)) +>over : Symbol(over, Decl(excessPropertyCheckWithUnions.ts, 43, 3)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 47, 8)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 47, 14)) +>first : Symbol(first, Decl(excessPropertyCheckWithUnions.ts, 47, 20)) +>third : Symbol(third, Decl(excessPropertyCheckWithUnions.ts, 47, 33)) // Freshness disappears after spreading a union declare let t0: { a: any, b: any } | { d: any, e: any } ->t0 : Symbol(t0, Decl(excessPropertyCheckWithUnions.ts, 52, 11)) ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 52, 17)) ->b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 52, 25)) ->d : Symbol(d, Decl(excessPropertyCheckWithUnions.ts, 52, 38)) ->e : Symbol(e, Decl(excessPropertyCheckWithUnions.ts, 52, 46)) +>t0 : Symbol(t0, Decl(excessPropertyCheckWithUnions.ts, 50, 11)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 50, 17)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 50, 25)) +>d : Symbol(d, Decl(excessPropertyCheckWithUnions.ts, 50, 38)) +>e : Symbol(e, Decl(excessPropertyCheckWithUnions.ts, 50, 46)) declare let t1: { a: any, b: any, c: any } | { c: any, d: any, e: any } ->t1 : Symbol(t1, Decl(excessPropertyCheckWithUnions.ts, 53, 11)) ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 53, 17)) ->b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 53, 25)) ->c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 53, 33)) ->c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 53, 46)) ->d : Symbol(d, Decl(excessPropertyCheckWithUnions.ts, 53, 54)) ->e : Symbol(e, Decl(excessPropertyCheckWithUnions.ts, 53, 62)) +>t1 : Symbol(t1, Decl(excessPropertyCheckWithUnions.ts, 51, 11)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 51, 17)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 51, 25)) +>c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 51, 33)) +>c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 51, 46)) +>d : Symbol(d, Decl(excessPropertyCheckWithUnions.ts, 51, 54)) +>e : Symbol(e, Decl(excessPropertyCheckWithUnions.ts, 51, 62)) let t2 = { ...t1 } ->t2 : Symbol(t2, Decl(excessPropertyCheckWithUnions.ts, 54, 3)) ->t1 : Symbol(t1, Decl(excessPropertyCheckWithUnions.ts, 53, 11)) +>t2 : Symbol(t2, Decl(excessPropertyCheckWithUnions.ts, 52, 3)) +>t1 : Symbol(t1, Decl(excessPropertyCheckWithUnions.ts, 51, 11)) t0 = t2 ->t0 : Symbol(t0, Decl(excessPropertyCheckWithUnions.ts, 52, 11)) ->t2 : Symbol(t2, Decl(excessPropertyCheckWithUnions.ts, 54, 3)) +>t0 : Symbol(t0, Decl(excessPropertyCheckWithUnions.ts, 50, 11)) +>t2 : Symbol(t2, Decl(excessPropertyCheckWithUnions.ts, 52, 3)) // Nested excess property checks work with discriminated unions type AN = { a: string } | { c: string } ->AN : Symbol(AN, Decl(excessPropertyCheckWithUnions.ts, 55, 7)) ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 58, 11)) ->c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 58, 27)) +>AN : Symbol(AN, Decl(excessPropertyCheckWithUnions.ts, 53, 7)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 56, 11)) +>c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 56, 27)) type BN = { b: string } ->BN : Symbol(BN, Decl(excessPropertyCheckWithUnions.ts, 58, 39)) ->b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 59, 11)) +>BN : Symbol(BN, Decl(excessPropertyCheckWithUnions.ts, 56, 39)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 57, 11)) type AB = { kind: "A", n: AN } | { kind: "B", n: BN } ->AB : Symbol(AB, Decl(excessPropertyCheckWithUnions.ts, 59, 23)) ->kind : Symbol(kind, Decl(excessPropertyCheckWithUnions.ts, 60, 11)) ->n : Symbol(n, Decl(excessPropertyCheckWithUnions.ts, 60, 22)) ->AN : Symbol(AN, Decl(excessPropertyCheckWithUnions.ts, 55, 7)) ->kind : Symbol(kind, Decl(excessPropertyCheckWithUnions.ts, 60, 34)) ->n : Symbol(n, Decl(excessPropertyCheckWithUnions.ts, 60, 45)) ->BN : Symbol(BN, Decl(excessPropertyCheckWithUnions.ts, 58, 39)) +>AB : Symbol(AB, Decl(excessPropertyCheckWithUnions.ts, 57, 23)) +>kind : Symbol(kind, Decl(excessPropertyCheckWithUnions.ts, 58, 11)) +>n : Symbol(n, Decl(excessPropertyCheckWithUnions.ts, 58, 22)) +>AN : Symbol(AN, Decl(excessPropertyCheckWithUnions.ts, 53, 7)) +>kind : Symbol(kind, Decl(excessPropertyCheckWithUnions.ts, 58, 34)) +>n : Symbol(n, Decl(excessPropertyCheckWithUnions.ts, 58, 45)) +>BN : Symbol(BN, Decl(excessPropertyCheckWithUnions.ts, 56, 39)) const abab: AB = { ->abab : Symbol(abab, Decl(excessPropertyCheckWithUnions.ts, 61, 5)) ->AB : Symbol(AB, Decl(excessPropertyCheckWithUnions.ts, 59, 23)) +>abab : Symbol(abab, Decl(excessPropertyCheckWithUnions.ts, 59, 5)) +>AB : Symbol(AB, Decl(excessPropertyCheckWithUnions.ts, 57, 23)) kind: "A", ->kind : Symbol(kind, Decl(excessPropertyCheckWithUnions.ts, 61, 18)) +>kind : Symbol(kind, Decl(excessPropertyCheckWithUnions.ts, 59, 18)) n: { ->n : Symbol(n, Decl(excessPropertyCheckWithUnions.ts, 62, 14)) +>n : Symbol(n, Decl(excessPropertyCheckWithUnions.ts, 60, 14)) a: "a", ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 63, 8)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 61, 8)) b: "b", // excess -- kind: "A" ->b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 64, 15)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 62, 15)) } } const abac: AB = { ->abac : Symbol(abac, Decl(excessPropertyCheckWithUnions.ts, 68, 5)) ->AB : Symbol(AB, Decl(excessPropertyCheckWithUnions.ts, 59, 23)) +>abac : Symbol(abac, Decl(excessPropertyCheckWithUnions.ts, 66, 5)) +>AB : Symbol(AB, Decl(excessPropertyCheckWithUnions.ts, 57, 23)) kind: "A", ->kind : Symbol(kind, Decl(excessPropertyCheckWithUnions.ts, 68, 18)) +>kind : Symbol(kind, Decl(excessPropertyCheckWithUnions.ts, 66, 18)) n: { ->n : Symbol(n, Decl(excessPropertyCheckWithUnions.ts, 69, 14)) +>n : Symbol(n, Decl(excessPropertyCheckWithUnions.ts, 67, 14)) a: "a", ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 70, 8)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 68, 8)) c: "c", // ok -- kind: "A", an: { a: string } | { c: string } ->c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 71, 15)) +>c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 69, 15)) } } // Excess property checks must match all discriminable properties type Button = { tag: 'button'; type?: 'submit'; }; ->Button : Symbol(Button, Decl(excessPropertyCheckWithUnions.ts, 74, 1)) ->tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 77, 15)) ->type : Symbol(type, Decl(excessPropertyCheckWithUnions.ts, 77, 30)) +>Button : Symbol(Button, Decl(excessPropertyCheckWithUnions.ts, 72, 1)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 75, 15)) +>type : Symbol(type, Decl(excessPropertyCheckWithUnions.ts, 75, 30)) type Anchor = { tag: 'a'; type?: string; href: string }; ->Anchor : Symbol(Anchor, Decl(excessPropertyCheckWithUnions.ts, 77, 50)) ->tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 78, 15)) ->type : Symbol(type, Decl(excessPropertyCheckWithUnions.ts, 78, 25)) ->href : Symbol(href, Decl(excessPropertyCheckWithUnions.ts, 78, 40)) +>Anchor : Symbol(Anchor, Decl(excessPropertyCheckWithUnions.ts, 75, 50)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 76, 15)) +>type : Symbol(type, Decl(excessPropertyCheckWithUnions.ts, 76, 25)) +>href : Symbol(href, Decl(excessPropertyCheckWithUnions.ts, 76, 40)) type Union = Button | Anchor; ->Union : Symbol(Union, Decl(excessPropertyCheckWithUnions.ts, 78, 56)) ->Button : Symbol(Button, Decl(excessPropertyCheckWithUnions.ts, 74, 1)) ->Anchor : Symbol(Anchor, Decl(excessPropertyCheckWithUnions.ts, 77, 50)) +>Union : Symbol(Union, Decl(excessPropertyCheckWithUnions.ts, 76, 56)) +>Button : Symbol(Button, Decl(excessPropertyCheckWithUnions.ts, 72, 1)) +>Anchor : Symbol(Anchor, Decl(excessPropertyCheckWithUnions.ts, 75, 50)) const obj: Union = { ->obj : Symbol(obj, Decl(excessPropertyCheckWithUnions.ts, 81, 5)) ->Union : Symbol(Union, Decl(excessPropertyCheckWithUnions.ts, 78, 56)) +>obj : Symbol(obj, Decl(excessPropertyCheckWithUnions.ts, 79, 5)) +>Union : Symbol(Union, Decl(excessPropertyCheckWithUnions.ts, 76, 56)) tag: 'button', ->tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 81, 20)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 79, 20)) type: 'submit', ->type : Symbol(type, Decl(excessPropertyCheckWithUnions.ts, 82, 18)) +>type : Symbol(type, Decl(excessPropertyCheckWithUnions.ts, 80, 18)) // should have error here href: 'foo', ->href : Symbol(href, Decl(excessPropertyCheckWithUnions.ts, 83, 19)) +>href : Symbol(href, Decl(excessPropertyCheckWithUnions.ts, 81, 19)) }; // Repro from #34611 interface IValue { ->IValue : Symbol(IValue, Decl(excessPropertyCheckWithUnions.ts, 87, 2)) +>IValue : Symbol(IValue, Decl(excessPropertyCheckWithUnions.ts, 85, 2)) value: string ->value : Symbol(IValue.value, Decl(excessPropertyCheckWithUnions.ts, 91, 18)) +>value : Symbol(IValue.value, Decl(excessPropertyCheckWithUnions.ts, 89, 18)) } -interface StringKeys { ->StringKeys : Symbol(StringKeys, Decl(excessPropertyCheckWithUnions.ts, 93, 1)) +interface StringKeys { +>StringKeys : Symbol(StringKeys, Decl(excessPropertyCheckWithUnions.ts, 91, 1)) [propertyName: string]: IValue; ->propertyName : Symbol(propertyName, Decl(excessPropertyCheckWithUnions.ts, 96, 5)) ->IValue : Symbol(IValue, Decl(excessPropertyCheckWithUnions.ts, 87, 2)) +>propertyName : Symbol(propertyName, Decl(excessPropertyCheckWithUnions.ts, 94, 5)) +>IValue : Symbol(IValue, Decl(excessPropertyCheckWithUnions.ts, 85, 2)) }; interface NumberKeys { ->NumberKeys : Symbol(NumberKeys, Decl(excessPropertyCheckWithUnions.ts, 97, 2)) +>NumberKeys : Symbol(NumberKeys, Decl(excessPropertyCheckWithUnions.ts, 95, 2)) [propertyName: number]: IValue; ->propertyName : Symbol(propertyName, Decl(excessPropertyCheckWithUnions.ts, 100, 5)) ->IValue : Symbol(IValue, Decl(excessPropertyCheckWithUnions.ts, 87, 2)) +>propertyName : Symbol(propertyName, Decl(excessPropertyCheckWithUnions.ts, 98, 5)) +>IValue : Symbol(IValue, Decl(excessPropertyCheckWithUnions.ts, 85, 2)) } type ObjectDataSpecification = StringKeys | NumberKeys; ->ObjectDataSpecification : Symbol(ObjectDataSpecification, Decl(excessPropertyCheckWithUnions.ts, 101, 1)) ->StringKeys : Symbol(StringKeys, Decl(excessPropertyCheckWithUnions.ts, 93, 1)) ->NumberKeys : Symbol(NumberKeys, Decl(excessPropertyCheckWithUnions.ts, 97, 2)) +>ObjectDataSpecification : Symbol(ObjectDataSpecification, Decl(excessPropertyCheckWithUnions.ts, 99, 1)) +>StringKeys : Symbol(StringKeys, Decl(excessPropertyCheckWithUnions.ts, 91, 1)) +>NumberKeys : Symbol(NumberKeys, Decl(excessPropertyCheckWithUnions.ts, 95, 2)) const dataSpecification: ObjectDataSpecification = { // Error ->dataSpecification : Symbol(dataSpecification, Decl(excessPropertyCheckWithUnions.ts, 106, 5)) ->ObjectDataSpecification : Symbol(ObjectDataSpecification, Decl(excessPropertyCheckWithUnions.ts, 101, 1)) +>dataSpecification : Symbol(dataSpecification, Decl(excessPropertyCheckWithUnions.ts, 104, 5)) +>ObjectDataSpecification : Symbol(ObjectDataSpecification, Decl(excessPropertyCheckWithUnions.ts, 99, 1)) foo: "asdfsadffsd" ->foo : Symbol(foo, Decl(excessPropertyCheckWithUnions.ts, 106, 52)) +>foo : Symbol(foo, Decl(excessPropertyCheckWithUnions.ts, 104, 52)) }; // Repro from #34611 const obj1: { [x: string]: number } | { [x: number]: number } = { a: 'abc' }; // Error ->obj1 : Symbol(obj1, Decl(excessPropertyCheckWithUnions.ts, 112, 5)) ->x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 112, 15)) ->x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 112, 41)) ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 112, 65)) +>obj1 : Symbol(obj1, Decl(excessPropertyCheckWithUnions.ts, 110, 5)) +>x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 110, 15)) +>x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 110, 41)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 110, 65)) const obj2: { [x: string]: number } | { a: number } = { a: 5, c: 'abc' }; // Error ->obj2 : Symbol(obj2, Decl(excessPropertyCheckWithUnions.ts, 113, 5)) ->x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 113, 15)) ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 113, 39)) ->a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 113, 55)) ->c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 113, 61)) +>obj2 : Symbol(obj2, Decl(excessPropertyCheckWithUnions.ts, 111, 5)) +>x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 111, 15)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 111, 39)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 111, 55)) +>c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 111, 61)) // Repro from #33732 interface I1 { ->I1 : Symbol(I1, Decl(excessPropertyCheckWithUnions.ts, 113, 73)) +>I1 : Symbol(I1, Decl(excessPropertyCheckWithUnions.ts, 111, 73)) prop1: string; ->prop1 : Symbol(I1.prop1, Decl(excessPropertyCheckWithUnions.ts, 117, 14)) +>prop1 : Symbol(I1.prop1, Decl(excessPropertyCheckWithUnions.ts, 115, 14)) } interface I2 { ->I2 : Symbol(I2, Decl(excessPropertyCheckWithUnions.ts, 119, 1)) +>I2 : Symbol(I2, Decl(excessPropertyCheckWithUnions.ts, 117, 1)) prop2: string; ->prop2 : Symbol(I2.prop2, Decl(excessPropertyCheckWithUnions.ts, 121, 14)) +>prop2 : Symbol(I2.prop2, Decl(excessPropertyCheckWithUnions.ts, 119, 14)) } interface I3 extends Record { ->I3 : Symbol(I3, Decl(excessPropertyCheckWithUnions.ts, 123, 1)) +>I3 : Symbol(I3, Decl(excessPropertyCheckWithUnions.ts, 121, 1)) >Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) } type Properties = ->Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 127, 1)) +>Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 125, 1)) | { [key: string]: never } ->key : Symbol(key, Decl(excessPropertyCheckWithUnions.ts, 130, 9)) +>key : Symbol(key, Decl(excessPropertyCheckWithUnions.ts, 128, 9)) | I1 ->I1 : Symbol(I1, Decl(excessPropertyCheckWithUnions.ts, 113, 73)) +>I1 : Symbol(I1, Decl(excessPropertyCheckWithUnions.ts, 111, 73)) | I2 ->I2 : Symbol(I2, Decl(excessPropertyCheckWithUnions.ts, 119, 1)) +>I2 : Symbol(I2, Decl(excessPropertyCheckWithUnions.ts, 117, 1)) | I3 ->I3 : Symbol(I3, Decl(excessPropertyCheckWithUnions.ts, 123, 1)) +>I3 : Symbol(I3, Decl(excessPropertyCheckWithUnions.ts, 121, 1)) ; declare const prop1: string; ->prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 137, 13)) +>prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 135, 13)) declare const prop2: string | undefined; ->prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 138, 13)) +>prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 136, 13)) function F1(_arg: { props: Properties }) { } ->F1 : Symbol(F1, Decl(excessPropertyCheckWithUnions.ts, 138, 40)) ->_arg : Symbol(_arg, Decl(excessPropertyCheckWithUnions.ts, 140, 12)) ->props : Symbol(props, Decl(excessPropertyCheckWithUnions.ts, 140, 19)) ->Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 127, 1)) +>F1 : Symbol(F1, Decl(excessPropertyCheckWithUnions.ts, 136, 40)) +>_arg : Symbol(_arg, Decl(excessPropertyCheckWithUnions.ts, 138, 12)) +>props : Symbol(props, Decl(excessPropertyCheckWithUnions.ts, 138, 19)) +>Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 125, 1)) F1({ ->F1 : Symbol(F1, Decl(excessPropertyCheckWithUnions.ts, 138, 40)) +>F1 : Symbol(F1, Decl(excessPropertyCheckWithUnions.ts, 136, 40)) props: { ->props : Symbol(props, Decl(excessPropertyCheckWithUnions.ts, 141, 4)) +>props : Symbol(props, Decl(excessPropertyCheckWithUnions.ts, 139, 4)) prop1, ->prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 142, 12)) +>prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 140, 12)) prop2, ->prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 143, 14)) +>prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 141, 14)) }, }); function F2(_props: Properties) { } ->F2 : Symbol(F2, Decl(excessPropertyCheckWithUnions.ts, 146, 3)) ->_props : Symbol(_props, Decl(excessPropertyCheckWithUnions.ts, 148, 12)) ->Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 127, 1)) +>F2 : Symbol(F2, Decl(excessPropertyCheckWithUnions.ts, 144, 3)) +>_props : Symbol(_props, Decl(excessPropertyCheckWithUnions.ts, 146, 12)) +>Properties : Symbol(Properties, Decl(excessPropertyCheckWithUnions.ts, 125, 1)) F2({ ->F2 : Symbol(F2, Decl(excessPropertyCheckWithUnions.ts, 146, 3)) +>F2 : Symbol(F2, Decl(excessPropertyCheckWithUnions.ts, 144, 3)) prop1, ->prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 149, 4)) +>prop1 : Symbol(prop1, Decl(excessPropertyCheckWithUnions.ts, 147, 4)) prop2, ->prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 150, 10)) +>prop2 : Symbol(prop2, Decl(excessPropertyCheckWithUnions.ts, 148, 10)) }); diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.types b/tests/baselines/reference/excessPropertyCheckWithUnions.types index 2b012e13eb14c..699a13fb48640 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.types +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.types @@ -126,9 +126,7 @@ amb = { tag: "A", y: 12, extra: 12 } >extra : number >12 : 12 -// assignability errors still work. -// But note that the error for `z: true` is the fallback one of reporting on -// the last constituent since assignability error reporting can't find a single best discriminant either. +// assignability errors still work amb = { tag: "A" } >amb = { tag: "A" } : { tag: "A"; } >amb : Ambiguous @@ -137,12 +135,12 @@ amb = { tag: "A" } >"A" : "A" amb = { tag: "A", z: true } ->amb = { tag: "A", z: true } : { tag: "A"; z: true; } +>amb = { tag: "A", z: true } : { tag: "A"; z: boolean; } >amb : Ambiguous ->{ tag: "A", z: true } : { tag: "A"; z: true; } +>{ tag: "A", z: true } : { tag: "A"; z: boolean; } >tag : "A" >"A" : "A" ->z : true +>z : boolean >true : true type Overlapping = @@ -319,7 +317,7 @@ interface IValue { >value : string } -interface StringKeys { +interface StringKeys { [propertyName: string]: IValue; >propertyName : string diff --git a/tests/baselines/reference/objectLiteralNormalization.errors.txt b/tests/baselines/reference/objectLiteralNormalization.errors.txt index e057800725e21..a6ffc718f913c 100644 --- a/tests/baselines/reference/objectLiteralNormalization.errors.txt +++ b/tests/baselines/reference/objectLiteralNormalization.errors.txt @@ -1,6 +1,7 @@ tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts(7,14): error TS2322: Type 'number' is not assignable to type 'string'. tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts(8,1): error TS2322: Type '{ b: string; }' is not assignable to type '{ a: number; b?: undefined; c?: undefined; } | { a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'. - Type '{ b: string; }' is missing the following properties from type '{ a: number; b: string; c: boolean; }': a, c + Type '{ b: string; }' is not assignable to type '{ a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'. + Type '{ b: string; }' is missing the following properties from type '{ a: number; b: string; c: boolean; }': a, c tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts(9,1): error TS2322: Type '{ c: true; }' is not assignable to type '{ a: number; b?: undefined; c?: undefined; } | { a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'. Type '{ c: true; }' is missing the following properties from type '{ a: number; b: string; c: boolean; }': a, b tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts(17,1): error TS2322: Type '{ a: string; b: number; }' is not assignable to type '{ a: number; b: number; } | { a: string; b?: undefined; } | { a?: undefined; b?: undefined; }'. @@ -27,7 +28,8 @@ tests/cases/conformance/expressions/objectLiterals/objectLiteralNormalization.ts a1 = { b: "y" }; // Error ~~ !!! error TS2322: Type '{ b: string; }' is not assignable to type '{ a: number; b?: undefined; c?: undefined; } | { a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'. -!!! error TS2322: Type '{ b: string; }' is missing the following properties from type '{ a: number; b: string; c: boolean; }': a, c +!!! error TS2322: Type '{ b: string; }' is not assignable to type '{ a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'. +!!! error TS2322: Type '{ b: string; }' is missing the following properties from type '{ a: number; b: string; c: boolean; }': a, c a1 = { c: true }; // Error ~~ !!! error TS2322: Type '{ c: true; }' is not assignable to type '{ a: number; b?: undefined; c?: undefined; } | { a: number; b: string; c?: undefined; } | { a: number; b: string; c: boolean; }'. diff --git a/tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts b/tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts index 5f7abedc1f42a..518a7a930aafd 100644 --- a/tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts +++ b/tests/cases/compiler/excessPropertyCheckWithMultipleDiscriminants.ts @@ -42,7 +42,7 @@ const a: DisjointDiscriminants = { p4: "hello" }; -// This has no excess error because variant one and three are both applicable. +// This has excess error because variant two is not applicable. const b: DisjointDiscriminants = { p1: 'left', p2: true, @@ -57,3 +57,38 @@ const c: DisjointDiscriminants = { p3: 42, p4: "hello" }; + +// Repro from #51873 + +interface Common { + type: "A" | "B" | "C" | "D"; + n: number; +} +interface A { + type: "A"; + a?: number; +} +interface B { + type: "B"; + b?: number; +} + +type CommonWithOverlappingOptionals = Common | (Common & A) | (Common & B); + +// Should reject { b } because reduced to Common | (Common & A) +const c1: CommonWithOverlappingOptionals = { + type: "A", + n: 1, + a: 1, + b: 1 // excess property +} + +type CommonWithDisjointOverlappingOptionals = Common | A | B; + +// Should still reject { b } because reduced to Common | A, even though these are now disjoint +const c2: CommonWithDisjointOverlappingOptionals = { + type: "A", + n: 1, + a: 1, + b: 1 // excess property +} diff --git a/tests/cases/compiler/excessPropertyCheckWithUnions.ts b/tests/cases/compiler/excessPropertyCheckWithUnions.ts index 4f8ea87a3ceb2..6f26597ae912c 100644 --- a/tests/cases/compiler/excessPropertyCheckWithUnions.ts +++ b/tests/cases/compiler/excessPropertyCheckWithUnions.ts @@ -34,9 +34,7 @@ amb = { tag: "A", x: "hi", y: 12 } amb = { tag: "A", x: "hi", extra: 12 } amb = { tag: "A", y: 12, extra: 12 } -// assignability errors still work. -// But note that the error for `z: true` is the fallback one of reporting on -// the last constituent since assignability error reporting can't find a single best discriminant either. +// assignability errors still work amb = { tag: "A" } amb = { tag: "A", z: true } @@ -94,7 +92,7 @@ interface IValue { value: string } -interface StringKeys { +interface StringKeys { [propertyName: string]: IValue; };