From b94fd85339dae18c04be4d3f7d0d2a6ea0d4f763 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 21 Jun 2018 21:39:54 -0700 Subject: [PATCH 1/4] Added test. --- .../errorsOnWeakTypeIntersectionTargets01.ts | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts diff --git a/tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts b/tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts new file mode 100644 index 0000000000000..b64305d401553 --- /dev/null +++ b/tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts @@ -0,0 +1,40 @@ +interface A { + a: number; +} + +interface B { + b?: string; +} + +// 'b' is incompatible. +export let x1: A & B = { + a: 0, + b: 12, +} + +// 'a' is incompatible, 'b' is present and compatible. +export let x2: A & B = { + a: "hello", + b: "hello", +} + +// 'a' is incompatible, 'b' is absent. +export let x3: A & B = { + a: "hello", +} + +// Both 'a' and 'b' are incompatible +export let x4: A & B = { + a: "hello", + b: 0, +} + +// 'b' is compatible, 'a' is missing +export let x5: A & B = { + b: 0, +} + +// 'b' is incompatible, 'a' is missing +export let x6: A & B = { + b: "", +} From 007d28daa6681cc52acd3ff50fb302ac35c9d7c0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 21 Jun 2018 21:42:14 -0700 Subject: [PATCH 2/4] Accepted baselines. --- ...OnWeakTypeIntersectionTargets01.errors.txt | 94 ++++++++++++++++++ .../errorsOnWeakTypeIntersectionTargets01.js | 73 ++++++++++++++ ...orsOnWeakTypeIntersectionTargets01.symbols | 84 ++++++++++++++++ ...rrorsOnWeakTypeIntersectionTargets01.types | 99 +++++++++++++++++++ 4 files changed, 350 insertions(+) create mode 100644 tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.errors.txt create mode 100644 tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.js create mode 100644 tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.symbols create mode 100644 tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.types diff --git a/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.errors.txt b/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.errors.txt new file mode 100644 index 0000000000000..974050070891f --- /dev/null +++ b/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.errors.txt @@ -0,0 +1,94 @@ +tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts(10,12): error TS2322: Type '{ a: number; b: number; }' is not assignable to type 'A & B'. + Type '{ a: number; b: number; }' is not assignable to type 'B'. + Types of property 'b' are incompatible. + Type 'number' is not assignable to type 'string'. +tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts(16,12): error TS2322: Type '{ a: string; b: string; }' is not assignable to type 'A & B'. + Type '{ a: string; b: string; }' is not assignable to type 'A'. + Types of property 'a' are incompatible. + Type 'string' is not assignable to type 'number'. +tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts(22,12): error TS2322: Type '{ a: string; }' is not assignable to type 'A & B'. + Type '{ a: string; }' is not assignable to type 'A'. + Types of property 'a' are incompatible. + Type 'string' is not assignable to type 'number'. +tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts(27,12): error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'A & B'. + Type '{ a: string; b: number; }' is not assignable to type 'A'. + Types of property 'a' are incompatible. + Type 'string' is not assignable to type 'number'. +tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts(33,12): error TS2322: Type '{ b: number; }' is not assignable to type 'A & B'. + Type '{ b: number; }' is not assignable to type 'A'. + Property 'a' is missing in type '{ b: number; }'. +tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts(38,12): error TS2322: Type '{ b: string; }' is not assignable to type 'A & B'. + Type '{ b: string; }' is not assignable to type 'A'. + Property 'a' is missing in type '{ b: string; }'. + + +==== tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts (6 errors) ==== + interface A { + a: number; + } + + interface B { + b?: string; + } + + // 'b' is incompatible. + export let x1: A & B = { + ~~ +!!! error TS2322: Type '{ a: number; b: number; }' is not assignable to type 'A & B'. +!!! error TS2322: Type '{ a: number; b: number; }' is not assignable to type 'B'. +!!! error TS2322: Types of property 'b' are incompatible. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + a: 0, + b: 12, + } + + // 'a' is incompatible, 'b' is present and compatible. + export let x2: A & B = { + ~~ +!!! error TS2322: Type '{ a: string; b: string; }' is not assignable to type 'A & B'. +!!! error TS2322: Type '{ a: string; b: string; }' is not assignable to type 'A'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type 'string' is not assignable to type 'number'. + a: "hello", + b: "hello", + } + + // 'a' is incompatible, 'b' is absent. + export let x3: A & B = { + ~~ +!!! error TS2322: Type '{ a: string; }' is not assignable to type 'A & B'. +!!! error TS2322: Type '{ a: string; }' is not assignable to type 'A'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type 'string' is not assignable to type 'number'. + a: "hello", + } + + // Both 'a' and 'b' are incompatible + export let x4: A & B = { + ~~ +!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'A & B'. +!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type 'A'. +!!! error TS2322: Types of property 'a' are incompatible. +!!! error TS2322: Type 'string' is not assignable to type 'number'. + a: "hello", + b: 0, + } + + // 'b' is compatible, 'a' is missing + export let x5: A & B = { + ~~ +!!! error TS2322: Type '{ b: number; }' is not assignable to type 'A & B'. +!!! error TS2322: Type '{ b: number; }' is not assignable to type 'A'. +!!! error TS2322: Property 'a' is missing in type '{ b: number; }'. + b: 0, + } + + // 'b' is incompatible, 'a' is missing + export let x6: A & B = { + ~~ +!!! error TS2322: Type '{ b: string; }' is not assignable to type 'A & B'. +!!! error TS2322: Type '{ b: string; }' is not assignable to type 'A'. +!!! error TS2322: Property 'a' is missing in type '{ b: string; }'. + b: "", + } + \ No newline at end of file diff --git a/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.js b/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.js new file mode 100644 index 0000000000000..d8b8df7566542 --- /dev/null +++ b/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.js @@ -0,0 +1,73 @@ +//// [errorsOnWeakTypeIntersectionTargets01.ts] +interface A { + a: number; +} + +interface B { + b?: string; +} + +// 'b' is incompatible. +export let x1: A & B = { + a: 0, + b: 12, +} + +// 'a' is incompatible, 'b' is present and compatible. +export let x2: A & B = { + a: "hello", + b: "hello", +} + +// 'a' is incompatible, 'b' is absent. +export let x3: A & B = { + a: "hello", +} + +// Both 'a' and 'b' are incompatible +export let x4: A & B = { + a: "hello", + b: 0, +} + +// 'b' is compatible, 'a' is missing +export let x5: A & B = { + b: 0, +} + +// 'b' is incompatible, 'a' is missing +export let x6: A & B = { + b: "", +} + + +//// [errorsOnWeakTypeIntersectionTargets01.js] +"use strict"; +exports.__esModule = true; +// 'b' is incompatible. +exports.x1 = { + a: 0, + b: 12 +}; +// 'a' is incompatible, 'b' is present and compatible. +exports.x2 = { + a: "hello", + b: "hello" +}; +// 'a' is incompatible, 'b' is absent. +exports.x3 = { + a: "hello" +}; +// Both 'a' and 'b' are incompatible +exports.x4 = { + a: "hello", + b: 0 +}; +// 'b' is compatible, 'a' is missing +exports.x5 = { + b: 0 +}; +// 'b' is incompatible, 'a' is missing +exports.x6 = { + b: "" +}; diff --git a/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.symbols b/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.symbols new file mode 100644 index 0000000000000..ae3e65e9c2004 --- /dev/null +++ b/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.symbols @@ -0,0 +1,84 @@ +=== tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts === +interface A { +>A : Symbol(A, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 0, 0)) + + a: number; +>a : Symbol(A.a, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 0, 13)) +} + +interface B { +>B : Symbol(B, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 2, 1)) + + b?: string; +>b : Symbol(B.b, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 4, 13)) +} + +// 'b' is incompatible. +export let x1: A & B = { +>x1 : Symbol(x1, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 9, 10)) +>A : Symbol(A, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 0, 0)) +>B : Symbol(B, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 2, 1)) + + a: 0, +>a : Symbol(a, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 9, 24)) + + b: 12, +>b : Symbol(b, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 10, 9)) +} + +// 'a' is incompatible, 'b' is present and compatible. +export let x2: A & B = { +>x2 : Symbol(x2, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 15, 10)) +>A : Symbol(A, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 0, 0)) +>B : Symbol(B, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 2, 1)) + + a: "hello", +>a : Symbol(a, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 15, 24)) + + b: "hello", +>b : Symbol(b, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 16, 15)) +} + +// 'a' is incompatible, 'b' is absent. +export let x3: A & B = { +>x3 : Symbol(x3, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 21, 10)) +>A : Symbol(A, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 0, 0)) +>B : Symbol(B, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 2, 1)) + + a: "hello", +>a : Symbol(a, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 21, 24)) +} + +// Both 'a' and 'b' are incompatible +export let x4: A & B = { +>x4 : Symbol(x4, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 26, 10)) +>A : Symbol(A, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 0, 0)) +>B : Symbol(B, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 2, 1)) + + a: "hello", +>a : Symbol(a, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 26, 24)) + + b: 0, +>b : Symbol(b, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 27, 15)) +} + +// 'b' is compatible, 'a' is missing +export let x5: A & B = { +>x5 : Symbol(x5, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 32, 10)) +>A : Symbol(A, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 0, 0)) +>B : Symbol(B, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 2, 1)) + + b: 0, +>b : Symbol(b, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 32, 24)) +} + +// 'b' is incompatible, 'a' is missing +export let x6: A & B = { +>x6 : Symbol(x6, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 37, 10)) +>A : Symbol(A, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 0, 0)) +>B : Symbol(B, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 2, 1)) + + b: "", +>b : Symbol(b, Decl(errorsOnWeakTypeIntersectionTargets01.ts, 37, 24)) +} + diff --git a/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.types b/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.types new file mode 100644 index 0000000000000..e59c7ccfcf910 --- /dev/null +++ b/tests/baselines/reference/errorsOnWeakTypeIntersectionTargets01.types @@ -0,0 +1,99 @@ +=== tests/cases/compiler/errorsOnWeakTypeIntersectionTargets01.ts === +interface A { +>A : A + + a: number; +>a : number +} + +interface B { +>B : B + + b?: string; +>b : string +} + +// 'b' is incompatible. +export let x1: A & B = { +>x1 : A & B +>A : A +>B : B +>{ a: 0, b: 12,} : { a: number; b: number; } + + a: 0, +>a : number +>0 : 0 + + b: 12, +>b : number +>12 : 12 +} + +// 'a' is incompatible, 'b' is present and compatible. +export let x2: A & B = { +>x2 : A & B +>A : A +>B : B +>{ a: "hello", b: "hello",} : { a: string; b: string; } + + a: "hello", +>a : string +>"hello" : "hello" + + b: "hello", +>b : string +>"hello" : "hello" +} + +// 'a' is incompatible, 'b' is absent. +export let x3: A & B = { +>x3 : A & B +>A : A +>B : B +>{ a: "hello",} : { a: string; } + + a: "hello", +>a : string +>"hello" : "hello" +} + +// Both 'a' and 'b' are incompatible +export let x4: A & B = { +>x4 : A & B +>A : A +>B : B +>{ a: "hello", b: 0,} : { a: string; b: number; } + + a: "hello", +>a : string +>"hello" : "hello" + + b: 0, +>b : number +>0 : 0 +} + +// 'b' is compatible, 'a' is missing +export let x5: A & B = { +>x5 : A & B +>A : A +>B : B +>{ b: 0,} : { b: number; } + + b: 0, +>b : number +>0 : 0 +} + +// 'b' is incompatible, 'a' is missing +export let x6: A & B = { +>x6 : A & B +>A : A +>B : B +>{ b: "",} : { b: string; } + + b: "", +>b : string +>"" : "" +} + From a001ab58b79a83c4ecf78e17be59f262f5305871 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 21 Jun 2018 21:43:04 -0700 Subject: [PATCH 3/4] Avoid printing out useless weak (but still important for society as a whole) types when reporting errors. Summary: When a source type still isn't assignable to an intersection with all the weak types removed, we can generally assume that the "strong" types are the ones that are more interesting to report on. As an improvement, we try to keep them around and remove weak types which we believe will cause noise. --- src/compiler/checker.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 57e34d86080a7..759bb2bb6b82f 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10616,6 +10616,7 @@ namespace ts { } let result = Ternary.False; + let strongIntersectionTarget: Type | undefined; const saveErrorInfo = errorInfo; const saveIsIntersectionConstituent = isIntersectionConstituent; isIntersectionConstituent = false; @@ -10635,6 +10636,23 @@ namespace ts { else if (target.flags & TypeFlags.Intersection) { isIntersectionConstituent = true; result = typeRelatedToEachType(source, target as IntersectionType, reportErrors); + + // We don't report errors at first. If the types aren't related then we'll try to see if + // the intersection contains any "weak" types that aren't contributing anything to relating the two types + // (but carefully in case *all* the types in the target are weak). + // If so, we'll drop them and report the error on a smaller intersection which should be more readable. + // If not, re-trigger the original relationship check. + result = typeRelatedToEachType(source, target as IntersectionType, /*reportErrors*/ false); + if (reportErrors && !result && some((target as IntersectionType).types, isWeakType) && !isWeakType(target)) { + const constituents = filter((target as IntersectionType).types, t => !isWeakType(t)); + Debug.assert(!!constituents.length, "Should have at least one non-weak constituent result."); + Debug.assert(constituents.length < (target as IntersectionType).types.length, "Should have fewer constituents"); + strongIntersectionTarget = getIntersectionType(constituents); + if (isRelatedTo(source, strongIntersectionTarget, reportErrors)) { + typeRelatedToEachType(source, target as IntersectionType, reportErrors); + strongIntersectionTarget = undefined; + } + } } else if (source.flags & TypeFlags.Intersection) { // Check to see if any constituents of the intersection are immediately related to the target. @@ -10679,6 +10697,11 @@ namespace ts { isIntersectionConstituent = saveIsIntersectionConstituent; if (!result && reportErrors) { + if (strongIntersectionTarget) { + // Should have reported an error above. + return result; + } + if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Primitive) { tryElaborateErrorsForPrimitivesAndObjects(source, target); } From 7054e73ee7b28e425dc2242758c13aa1468093d4 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 21 Jun 2018 21:47:39 -0700 Subject: [PATCH 4/4] Accepted baselines. --- .../checkJsxChildrenProperty14.errors.txt | 14 ++-- .../checkJsxChildrenProperty2.errors.txt | 78 +++++++++++-------- .../checkJsxChildrenProperty4.errors.txt | 14 ++-- .../checkJsxChildrenProperty5.errors.txt | 34 ++++---- .../checkJsxChildrenProperty7.errors.txt | 50 ++++++------ ...StringLiteralsInJsxAttributes02.errors.txt | 12 ++- ...OnWeakTypeIntersectionTargets01.errors.txt | 30 +++---- .../tsxAttributeResolution12.errors.txt | 8 +- ...ponentWithDefaultTypeParameter3.errors.txt | 6 +- ...tsxSpreadAttributesResolution12.errors.txt | 10 ++- ...tsxSpreadAttributesResolution16.errors.txt | 6 +- .../tsxSpreadAttributesResolution2.errors.txt | 32 +++++--- .../tsxSpreadAttributesResolution5.errors.txt | 10 ++- .../tsxSpreadAttributesResolution6.errors.txt | 6 +- ...elessFunctionComponentOverload4.errors.txt | 28 ++++--- ...elessFunctionComponentOverload5.errors.txt | 30 ++++--- ...tsxStatelessFunctionComponents1.errors.txt | 12 ++- ...ionComponentsWithTypeArguments2.errors.txt | 4 +- ...ionComponentsWithTypeArguments4.errors.txt | 10 ++- .../reference/tsxUnionElementType6.errors.txt | 12 ++- 20 files changed, 242 insertions(+), 164 deletions(-) diff --git a/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt b/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt index cf999188ebc59..26676a08b2996 100644 --- a/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt +++ b/tests/baselines/reference/checkJsxChildrenProperty14.errors.txt @@ -1,7 +1,8 @@ tests/cases/conformance/jsx/file.tsx(42,11): error TS2322: Type '{ children: Element[]; a: number; b: string; }' is not assignable to type 'SingleChildProp'. - Types of property 'children' are incompatible. - Type 'Element[]' is not assignable to type 'Element'. - Property 'type' is missing in type 'Element[]'. + Type '{ children: Element[]; a: number; b: string; }' is not assignable to type 'SingleChildProp'. + Types of property 'children' are incompatible. + Type 'Element[]' is not assignable to type 'Element'. + Property 'type' is missing in type 'Element[]'. ==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== @@ -49,6 +50,7 @@ tests/cases/conformance/jsx/file.tsx(42,11): error TS2322: Type '{ children: Ele let k5 = <>