From 595cb11f22d4bb3fe4bbf3e4d0366c6b5903c57a Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Jun 2017 11:01:32 -0700 Subject: [PATCH 1/6] Excess property checks for discriminated unions This uses the same code as #14006, which improves error messages for discriminated unions. --- src/compiler/checker.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 266fbe573beb9..ddfebd84ad69d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8964,6 +8964,13 @@ namespace ts { (isTypeSubsetOf(globalObjectType, target) || (!isComparingJsxAttributes && isEmptyObjectType(target)))) { return false; } + if (target.flags & TypeFlags.Union) { + const discriminantType = findMatchingDiscriminantType(source, target as UnionType); + if (discriminantType) { + // check excess properties against discriminant type only, not the entire union + return hasExcessProperties(source, discriminantType, reportErrors); + } + } for (const prop of getPropertiesOfObjectType(source)) { if (!isKnownProperty(target, prop.name, isComparingJsxAttributes)) { if (reportErrors) { From 8302ebcd8b2f493539abf6fd1d9f9f3718af252c Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 8 Jun 2017 11:03:52 -0700 Subject: [PATCH 2/6] Test:excess property checks--discriminated unions --- .../discriminatedUnionErrorMessage.errors.txt | 12 +++---- .../excessPropertyCheckWithUnions.errors.txt | 33 +++++++++++++++++++ .../excessPropertyCheckWithUnions.js | 19 +++++++++++ .../compiler/excessPropertyCheckWithUnions.ts | 12 +++++++ 4 files changed, 69 insertions(+), 7 deletions(-) create mode 100644 tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt create mode 100644 tests/baselines/reference/excessPropertyCheckWithUnions.js create mode 100644 tests/cases/compiler/excessPropertyCheckWithUnions.ts diff --git a/tests/baselines/reference/discriminatedUnionErrorMessage.errors.txt b/tests/baselines/reference/discriminatedUnionErrorMessage.errors.txt index 6f1eb511d3714..e54727befd0e1 100644 --- a/tests/baselines/reference/discriminatedUnionErrorMessage.errors.txt +++ b/tests/baselines/reference/discriminatedUnionErrorMessage.errors.txt @@ -1,6 +1,5 @@ -tests/cases/compiler/discriminatedUnionErrorMessage.ts(8,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'. - Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'. - Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'. +tests/cases/compiler/discriminatedUnionErrorMessage.ts(10,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'. + Object literal may only specify known properties, and 'x' does not exist in type 'Square'. ==== tests/cases/compiler/discriminatedUnionErrorMessage.ts (1 errors) ==== @@ -12,12 +11,11 @@ tests/cases/compiler/discriminatedUnionErrorMessage.ts(8,5): error TS2322: Type | Rectangle | Circle; let shape: Shape = { - ~~~~~ -!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'. -!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'. -!!! error TS2322: Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'. kind: "sq", x: 12, + ~~~~~ +!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'. +!!! error TS2322: Object literal may only specify known properties, and 'x' does not exist in type 'Square'. y: 13, } \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt new file mode 100644 index 0000000000000..1a01f8cb5b840 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -0,0 +1,33 @@ +tests/cases/compiler/excessPropertyCheckWithUnions.ts(10,30): error TS2322: Type '{ tag: "T"; a1: string; }' is not assignable to type 'ADT'. + Object literal may only specify known properties, and 'a1' does not exist in type '{ tag: "T"; }'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(11,21): error TS2322: Type '{ tag: "A"; d20: 12; }' is not assignable to type 'ADT'. + Object literal may only specify known properties, and 'd20' does not exist in type '{ tag: "A"; a1: string; }'. +tests/cases/compiler/excessPropertyCheckWithUnions.ts(12,1): error TS2322: Type '{ tag: "D"; }' is not assignable to type 'ADT'. + Type '{ tag: "D"; }' is not assignable to type '{ tag: "D"; d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; }'. + Property 'd20' is missing in type '{ tag: "D"; }'. + + +==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (3 errors) ==== + type ADT = { + tag: "A", + a1: string + } | { + tag: "D", + d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 + } | { + tag: "T", + } + let wrong: ADT = { tag: "T", a1: "extra" } + ~~~~~~~~~~~ +!!! error TS2322: Type '{ tag: "T"; a1: string; }' is not assignable to type 'ADT'. +!!! error TS2322: Object literal may only specify known properties, and 'a1' does not exist in type '{ tag: "T"; }'. + wrong = { tag: "A", d20: 12 } + ~~~~~~~ +!!! error TS2322: Type '{ tag: "A"; d20: 12; }' is not assignable to type 'ADT'. +!!! error TS2322: Object literal may only specify known properties, and 'd20' does not exist in type '{ tag: "A"; a1: string; }'. + wrong = { tag: "D" } + ~~~~~ +!!! error TS2322: Type '{ tag: "D"; }' is not assignable to type 'ADT'. +!!! error TS2322: Type '{ tag: "D"; }' is not assignable to type '{ tag: "D"; d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; }'. +!!! error TS2322: Property 'd20' is missing in type '{ tag: "D"; }'. + \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.js b/tests/baselines/reference/excessPropertyCheckWithUnions.js new file mode 100644 index 0000000000000..79c30bce9c769 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.js @@ -0,0 +1,19 @@ +//// [excessPropertyCheckWithUnions.ts] +type ADT = { + tag: "A", + a1: string +} | { + tag: "D", + d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 +} | { + tag: "T", +} +let wrong: ADT = { tag: "T", a1: "extra" } +wrong = { tag: "A", d20: 12 } +wrong = { tag: "D" } + + +//// [excessPropertyCheckWithUnions.js] +var wrong = { tag: "T", a1: "extra" }; +wrong = { tag: "A", d20: 12 }; +wrong = { tag: "D" }; diff --git a/tests/cases/compiler/excessPropertyCheckWithUnions.ts b/tests/cases/compiler/excessPropertyCheckWithUnions.ts new file mode 100644 index 0000000000000..8b1abf1b7640a --- /dev/null +++ b/tests/cases/compiler/excessPropertyCheckWithUnions.ts @@ -0,0 +1,12 @@ +type ADT = { + tag: "A", + a1: string +} | { + tag: "D", + d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 +} | { + tag: "T", +} +let wrong: ADT = { tag: "T", a1: "extra" } +wrong = { tag: "A", d20: 12 } +wrong = { tag: "D" } From c8d856a5d4489860114d809c010a52a56a5f91a7 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 9 Jun 2017 09:51:07 -0700 Subject: [PATCH 3/6] Correct excess property error on ambiguous discriminated unions --- src/compiler/checker.ts | 7 ++- .../excessPropertyCheckWithUnions.errors.txt | 58 ++++++++++++++++++- .../excessPropertyCheckWithUnions.js | 41 +++++++++++++ .../compiler/excessPropertyCheckWithUnions.ts | 28 +++++++++ 4 files changed, 132 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ddfebd84ad69d..559d6ae71baf1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9029,6 +9029,7 @@ namespace ts { function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) { const sourceProperties = getPropertiesOfObjectType(source); + let match: Type; if (sourceProperties) { for (const sourceProperty of sourceProperties) { if (isDiscriminantProperty(target, sourceProperty.name)) { @@ -9036,12 +9037,16 @@ namespace ts { for (const type of target.types) { const targetType = getTypeOfPropertyOfType(type, sourceProperty.name); if (targetType && isRelatedTo(sourceType, targetType)) { - return type; + if (match) { + return undefined; + } + match = type; } } } } } + return match; } function typeRelatedToEachType(source: Type, target: IntersectionType, reportErrors: boolean): Ternary { diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt index 1a01f8cb5b840..9714c90ba619b 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -5,9 +5,21 @@ 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'. Type '{ tag: "D"; }' is not assignable to type '{ tag: "D"; d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; }'. Property 'd20' is missing in type '{ tag: "D"; }'. +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'. +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: "C"; }'. + Types of property 'tag' are incompatible. + Type '"A"' is not assignable to type '"C"'. -==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (3 errors) ==== +==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (7 errors) ==== type ADT = { tag: "A", a1: string @@ -30,4 +42,48 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(12,1): error TS2322: Type !!! error TS2322: Type '{ tag: "D"; }' is not assignable to type 'ADT'. !!! error TS2322: Type '{ tag: "D"; }' is not assignable to type '{ tag: "D"; d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; }'. !!! error TS2322: Property 'd20' is missing in type '{ tag: "D"; }'. + + type Ambiguous = { + tag: "A", + x: string + } | { + tag: "A", + y: number + } | { + tag: "B", + z: boolean + } | { + tag: "C" + } + let amb: Ambiguous + // no error for ambiguous tag, even when it could satisfy both constituents at once + amb = { tag: "A", x: "hi" } + amb = { tag: "A", y: 12 } + amb = { tag: "A", x: "hi", y: 12 } + + // correctly error on excess property 'extra', even when ambiguous + 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'. + 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'. + + // 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. + 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"'. + 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: "C"; }'. +!!! error TS2322: Types of property 'tag' are incompatible. +!!! error TS2322: Type '"A"' is not assignable to type '"C"'. \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.js b/tests/baselines/reference/excessPropertyCheckWithUnions.js index 79c30bce9c769..fb0a5a6f81911 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.js +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.js @@ -11,9 +11,50 @@ type ADT = { let wrong: ADT = { tag: "T", a1: "extra" } wrong = { tag: "A", d20: 12 } wrong = { tag: "D" } + +type Ambiguous = { + tag: "A", + x: string +} | { + tag: "A", + y: number +} | { + tag: "B", + z: boolean +} | { + tag: "C" +} +let amb: Ambiguous +// no error for ambiguous tag, even when it could satisfy both constituents at once +amb = { tag: "A", x: "hi" } +amb = { tag: "A", y: 12 } +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. +amb = { tag: "A" } +amb = { tag: "A", z: true } //// [excessPropertyCheckWithUnions.js] var wrong = { tag: "T", a1: "extra" }; wrong = { tag: "A", d20: 12 }; wrong = { tag: "D" }; +var amb; +// no error for ambiguous tag, even when it could satisfy both constituents at once +amb = { tag: "A", x: "hi" }; +amb = { tag: "A", y: 12 }; +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. +amb = { tag: "A" }; +amb = { tag: "A", z: true }; diff --git a/tests/cases/compiler/excessPropertyCheckWithUnions.ts b/tests/cases/compiler/excessPropertyCheckWithUnions.ts index 8b1abf1b7640a..51f36d137fdda 100644 --- a/tests/cases/compiler/excessPropertyCheckWithUnions.ts +++ b/tests/cases/compiler/excessPropertyCheckWithUnions.ts @@ -10,3 +10,31 @@ type ADT = { let wrong: ADT = { tag: "T", a1: "extra" } wrong = { tag: "A", d20: 12 } wrong = { tag: "D" } + +type Ambiguous = { + tag: "A", + x: string +} | { + tag: "A", + y: number +} | { + tag: "B", + z: boolean +} | { + tag: "C" +} +let amb: Ambiguous +// no error for ambiguous tag, even when it could satisfy both constituents at once +amb = { tag: "A", x: "hi" } +amb = { tag: "A", y: 12 } +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. +amb = { tag: "A" } +amb = { tag: "A", z: true } From d04f4a93a7fff465eaf2da93931f1949f6e49a22 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 9 Jun 2017 15:42:14 -0700 Subject: [PATCH 4/6] Do not check excess properties for multi-discriminant unions --- src/compiler/checker.ts | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 559d6ae71baf1..f79e74746d453 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9028,20 +9028,19 @@ namespace ts { } function findMatchingDiscriminantType(source: Type, target: UnionOrIntersectionType) { - const sourceProperties = getPropertiesOfObjectType(source); let match: Type; + const sourceProperties = getPropertiesOfObjectType(source); if (sourceProperties) { - for (const sourceProperty of sourceProperties) { - if (isDiscriminantProperty(target, sourceProperty.name)) { - const sourceType = getTypeOfSymbol(sourceProperty); - for (const type of target.types) { - const targetType = getTypeOfPropertyOfType(type, sourceProperty.name); - if (targetType && isRelatedTo(sourceType, targetType)) { - if (match) { - return undefined; - } - match = type; + const sourceProperty = findSingleDiscriminantProperty(sourceProperties, target); + if (sourceProperty) { + const sourceType = getTypeOfSymbol(sourceProperty); + for (const type of target.types) { + const targetType = getTypeOfPropertyOfType(type, sourceProperty.name); + if (targetType && isRelatedTo(sourceType, targetType)) { + if (match) { + return undefined; } + match = type; } } } @@ -10839,6 +10838,19 @@ namespace ts { return false; } + function findSingleDiscriminantProperty(sourceProperties: Symbol[], target: Type): Symbol | undefined { + let result: Symbol; + for (const sourceProperty of sourceProperties) { + if (isDiscriminantProperty(target, sourceProperty.name)) { + if (result) { + return undefined; + } + result = sourceProperty; + } + } + return result; + } + function isOrContainsMatchingReference(source: Node, target: Node) { return isMatchingReference(source, target) || containsMatchingReference(source, target); } From 8a7186d1901197e9d050b1a200470c1cd1a065c5 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Fri, 9 Jun 2017 15:47:57 -0700 Subject: [PATCH 5/6] Add more excess property check tests for unions --- .../excessPropertyCheckWithUnions.errors.txt | 10 ++++++++++ .../reference/excessPropertyCheckWithUnions.js | 14 ++++++++++++++ .../compiler/excessPropertyCheckWithUnions.ts | 10 ++++++++++ 3 files changed, 34 insertions(+) diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt index 9714c90ba619b..3b7e5a787d2aa 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -86,4 +86,14 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(40,1): error TS2322: Type !!! error TS2322: Type '{ tag: "A"; z: true; }' 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"'. + + type Overlapping = + | { a: 1, b: 1, first: string } + | { a: 2, second: string } + | { b: 3, third: string } + let over: Overlapping + + // these two are not reported because there are two discriminant properties + over = { a: 1, b: 1, first: "ok", second: "error" } + over = { a: 1, b: 1, first: "ok", third: "error" } \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.js b/tests/baselines/reference/excessPropertyCheckWithUnions.js index fb0a5a6f81911..c6da660b52dde 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.js +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.js @@ -39,6 +39,16 @@ amb = { tag: "A", y: 12, extra: 12 } // the last constituent since assignability error reporting can't find a single best discriminant either. amb = { tag: "A" } amb = { tag: "A", z: true } + +type Overlapping = + | { a: 1, b: 1, first: string } + | { a: 2, second: string } + | { b: 3, third: string } +let over: Overlapping + +// these two are not reported because there are two discriminant properties +over = { a: 1, b: 1, first: "ok", second: "error" } +over = { a: 1, b: 1, first: "ok", third: "error" } //// [excessPropertyCheckWithUnions.js] @@ -58,3 +68,7 @@ amb = { tag: "A", y: 12, extra: 12 }; // the last constituent since assignability error reporting can't find a single best discriminant either. amb = { tag: "A" }; amb = { tag: "A", z: true }; +var over; +// these two are not reported because there are two discriminant properties +over = { a: 1, b: 1, first: "ok", second: "error" }; +over = { a: 1, b: 1, first: "ok", third: "error" }; diff --git a/tests/cases/compiler/excessPropertyCheckWithUnions.ts b/tests/cases/compiler/excessPropertyCheckWithUnions.ts index 51f36d137fdda..9a7968fe5114e 100644 --- a/tests/cases/compiler/excessPropertyCheckWithUnions.ts +++ b/tests/cases/compiler/excessPropertyCheckWithUnions.ts @@ -38,3 +38,13 @@ amb = { tag: "A", y: 12, extra: 12 } // the last constituent since assignability error reporting can't find a single best discriminant either. amb = { tag: "A" } amb = { tag: "A", z: true } + +type Overlapping = + | { a: 1, b: 1, first: string } + | { a: 2, second: string } + | { b: 3, third: string } +let over: Overlapping + +// these two are not reported because there are two discriminant properties +over = { a: 1, b: 1, first: "ok", second: "error" } +over = { a: 1, b: 1, first: "ok", third: "error" } From 97ee9516d671eb52fe96fa6694f6a2bbe12fcf29 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Thu, 5 Oct 2017 09:10:55 -0700 Subject: [PATCH 6/6] Update baselines --- .../excessPropertyCheckWithUnions.symbols | 144 +++++++++++++ .../excessPropertyCheckWithUnions.types | 196 ++++++++++++++++++ 2 files changed, 340 insertions(+) create mode 100644 tests/baselines/reference/excessPropertyCheckWithUnions.symbols create mode 100644 tests/baselines/reference/excessPropertyCheckWithUnions.types diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.symbols b/tests/baselines/reference/excessPropertyCheckWithUnions.symbols new file mode 100644 index 0000000000000..332166e396c8f --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.symbols @@ -0,0 +1,144 @@ +=== tests/cases/compiler/excessPropertyCheckWithUnions.ts === +type ADT = { +>ADT : Symbol(ADT, Decl(excessPropertyCheckWithUnions.ts, 0, 0)) + + tag: "A", +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 0, 12)) + + a1: string +>a1 : Symbol(a1, Decl(excessPropertyCheckWithUnions.ts, 1, 13)) + +} | { + tag: "D", +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 3, 5)) + + d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 +>d20 : Symbol(d20, Decl(excessPropertyCheckWithUnions.ts, 4, 13)) + +} | { + tag: "T", +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 6, 5)) +} +let wrong: ADT = { tag: "T", a1: "extra" } +>wrong : Symbol(wrong, Decl(excessPropertyCheckWithUnions.ts, 9, 3)) +>ADT : Symbol(ADT, Decl(excessPropertyCheckWithUnions.ts, 0, 0)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 9, 18)) +>a1 : Symbol(a1, Decl(excessPropertyCheckWithUnions.ts, 9, 28)) + +wrong = { tag: "A", d20: 12 } +>wrong : Symbol(wrong, Decl(excessPropertyCheckWithUnions.ts, 9, 3)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 10, 9)) +>d20 : Symbol(d20, Decl(excessPropertyCheckWithUnions.ts, 10, 19)) + +wrong = { tag: "D" } +>wrong : Symbol(wrong, Decl(excessPropertyCheckWithUnions.ts, 9, 3)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 11, 9)) + +type Ambiguous = { +>Ambiguous : Symbol(Ambiguous, Decl(excessPropertyCheckWithUnions.ts, 11, 20)) + + tag: "A", +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 13, 18)) + + x: string +>x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 14, 13)) + +} | { + tag: "A", +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 16, 5)) + + y: number +>y : Symbol(y, Decl(excessPropertyCheckWithUnions.ts, 17, 13)) + +} | { + tag: "B", +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 19, 5)) + + z: boolean +>z : Symbol(z, Decl(excessPropertyCheckWithUnions.ts, 20, 13)) + +} | { + tag: "C" +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 22, 5)) +} +let amb: Ambiguous +>amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) +>Ambiguous : Symbol(Ambiguous, Decl(excessPropertyCheckWithUnions.ts, 11, 20)) + +// no error for ambiguous tag, even when it could satisfy both constituents at once +amb = { tag: "A", x: "hi" } +>amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 27, 7)) +>x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 27, 17)) + +amb = { tag: "A", y: 12 } +>amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 28, 7)) +>y : Symbol(y, Decl(excessPropertyCheckWithUnions.ts, 28, 17)) + +amb = { tag: "A", x: "hi", y: 12 } +>amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 29, 7)) +>x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 29, 17)) +>y : Symbol(y, Decl(excessPropertyCheckWithUnions.ts, 29, 26)) + +// correctly error on excess property 'extra', even when ambiguous +amb = { tag: "A", x: "hi", extra: 12 } +>amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 32, 7)) +>x : Symbol(x, Decl(excessPropertyCheckWithUnions.ts, 32, 17)) +>extra : Symbol(extra, Decl(excessPropertyCheckWithUnions.ts, 32, 26)) + +amb = { tag: "A", y: 12, extra: 12 } +>amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 33, 7)) +>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. +amb = { tag: "A" } +>amb : Symbol(amb, Decl(excessPropertyCheckWithUnions.ts, 25, 3)) +>tag : Symbol(tag, Decl(excessPropertyCheckWithUnions.ts, 38, 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)) + +type Overlapping = +>Overlapping : Symbol(Overlapping, Decl(excessPropertyCheckWithUnions.ts, 39, 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: 2, second: string } +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 43, 7)) +>second : Symbol(second, Decl(excessPropertyCheckWithUnions.ts, 43, 13)) + + | { b: 3, third: string } +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 44, 7)) +>third : Symbol(third, Decl(excessPropertyCheckWithUnions.ts, 44, 13)) + +let over: Overlapping +>over : Symbol(over, Decl(excessPropertyCheckWithUnions.ts, 45, 3)) +>Overlapping : Symbol(Overlapping, Decl(excessPropertyCheckWithUnions.ts, 39, 27)) + +// these two are not reported because there are two discriminant properties +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 = { 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)) + diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.types b/tests/baselines/reference/excessPropertyCheckWithUnions.types new file mode 100644 index 0000000000000..1d6bdd32eb269 --- /dev/null +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.types @@ -0,0 +1,196 @@ +=== tests/cases/compiler/excessPropertyCheckWithUnions.ts === +type ADT = { +>ADT : ADT + + tag: "A", +>tag : "A" + + a1: string +>a1 : string + +} | { + tag: "D", +>tag : "D" + + d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 +>d20 : 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 + +} | { + tag: "T", +>tag : "T" +} +let wrong: ADT = { tag: "T", a1: "extra" } +>wrong : ADT +>ADT : ADT +>{ tag: "T", a1: "extra" } : { tag: "T"; a1: string; } +>tag : string +>"T" : "T" +>a1 : string +>"extra" : "extra" + +wrong = { tag: "A", d20: 12 } +>wrong = { tag: "A", d20: 12 } : { tag: "A"; d20: 12; } +>wrong : ADT +>{ tag: "A", d20: 12 } : { tag: "A"; d20: 12; } +>tag : string +>"A" : "A" +>d20 : number +>12 : 12 + +wrong = { tag: "D" } +>wrong = { tag: "D" } : { tag: "D"; } +>wrong : ADT +>{ tag: "D" } : { tag: "D"; } +>tag : string +>"D" : "D" + +type Ambiguous = { +>Ambiguous : Ambiguous + + tag: "A", +>tag : "A" + + x: string +>x : string + +} | { + tag: "A", +>tag : "A" + + y: number +>y : number + +} | { + tag: "B", +>tag : "B" + + z: boolean +>z : boolean + +} | { + tag: "C" +>tag : "C" +} +let amb: Ambiguous +>amb : Ambiguous +>Ambiguous : Ambiguous + +// no error for ambiguous tag, even when it could satisfy both constituents at once +amb = { tag: "A", x: "hi" } +>amb = { tag: "A", x: "hi" } : { tag: "A"; x: string; } +>amb : Ambiguous +>{ tag: "A", x: "hi" } : { tag: "A"; x: string; } +>tag : string +>"A" : "A" +>x : string +>"hi" : "hi" + +amb = { tag: "A", y: 12 } +>amb = { tag: "A", y: 12 } : { tag: "A"; y: number; } +>amb : Ambiguous +>{ tag: "A", y: 12 } : { tag: "A"; y: number; } +>tag : string +>"A" : "A" +>y : number +>12 : 12 + +amb = { tag: "A", x: "hi", y: 12 } +>amb = { tag: "A", x: "hi", y: 12 } : { tag: "A"; x: string; y: number; } +>amb : Ambiguous +>{ tag: "A", x: "hi", y: 12 } : { tag: "A"; x: string; y: number; } +>tag : string +>"A" : "A" +>x : string +>"hi" : "hi" +>y : number +>12 : 12 + +// correctly error on excess property 'extra', even when ambiguous +amb = { tag: "A", x: "hi", extra: 12 } +>amb = { tag: "A", x: "hi", extra: 12 } : { tag: "A"; x: string; extra: number; } +>amb : Ambiguous +>{ tag: "A", x: "hi", extra: 12 } : { tag: "A"; x: string; extra: number; } +>tag : string +>"A" : "A" +>x : string +>"hi" : "hi" +>extra : number +>12 : 12 + +amb = { tag: "A", y: 12, extra: 12 } +>amb = { tag: "A", y: 12, extra: 12 } : { tag: "A"; y: number; extra: number; } +>amb : Ambiguous +>{ tag: "A", y: 12, extra: 12 } : { tag: "A"; y: number; extra: number; } +>tag : string +>"A" : "A" +>y : number +>12 : 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. +amb = { tag: "A" } +>amb = { tag: "A" } : { tag: "A"; } +>amb : Ambiguous +>{ tag: "A" } : { tag: "A"; } +>tag : string +>"A" : "A" + +amb = { tag: "A", z: true } +>amb = { tag: "A", z: true } : { tag: "A"; z: true; } +>amb : Ambiguous +>{ tag: "A", z: true } : { tag: "A"; z: true; } +>tag : string +>"A" : "A" +>z : boolean +>true : true + +type Overlapping = +>Overlapping : Overlapping + + | { a: 1, b: 1, first: string } +>a : 1 +>b : 1 +>first : string + + | { a: 2, second: string } +>a : 2 +>second : string + + | { b: 3, third: string } +>b : 3 +>third : string + +let over: Overlapping +>over : Overlapping +>Overlapping : Overlapping + +// these two are not reported because there are two discriminant properties +over = { a: 1, b: 1, first: "ok", second: "error" } +>over = { a: 1, b: 1, first: "ok", second: "error" } : { a: 1; b: 1; first: string; second: string; } +>over : Overlapping +>{ a: 1, b: 1, first: "ok", second: "error" } : { a: 1; b: 1; first: string; second: string; } +>a : number +>1 : 1 +>b : number +>1 : 1 +>first : string +>"ok" : "ok" +>second : string +>"error" : "error" + +over = { a: 1, b: 1, first: "ok", third: "error" } +>over = { a: 1, b: 1, first: "ok", third: "error" } : { a: 1; b: 1; first: string; third: string; } +>over : Overlapping +>{ a: 1, b: 1, first: "ok", third: "error" } : { a: 1; b: 1; first: string; third: string; } +>a : number +>1 : 1 +>b : number +>1 : 1 +>first : string +>"ok" : "ok" +>third : string +>"error" : "error" +