diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index cf3bd49751a..558489b6e33 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5521,8 +5521,20 @@ namespace ts { // on either side that need to be prioritized. For example, A | B = (A | B) & (C | D) or // A & B = (A & B) | (C & D). if (source.flags & TypeFlags.Intersection) { - // If target is a union type the following check will report errors so we suppress them here - if (result = someTypeRelatedToType(source, target, reportErrors && !(target.flags & TypeFlags.Union))) { + // Check to see if any constituents of the intersection are immediately related to the target. + // + // Don't report errors though. Checking whether a constituent is related to the source is not actually + // useful and leads to some confusing error messages. Instead it is better to let the below checks + // take care of this, or to not elaborate at all. For instance, + // + // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors. + // + // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection + // than to report that 'D' is not assignable to 'A' or 'B'. + // + // - For a primitive type or type parameter (such as 'number = A & B') there is no point in + // breaking the intersection apart. + if (result = someTypeRelatedToType(source, target, /*reportErrors*/ false)) { return result; } } diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes01.errors.txt b/tests/baselines/reference/errorMessagesIntersectionTypes01.errors.txt new file mode 100644 index 00000000000..6b36f29a63e --- /dev/null +++ b/tests/baselines/reference/errorMessagesIntersectionTypes01.errors.txt @@ -0,0 +1,26 @@ +tests/cases/compiler/errorMessagesIntersectionTypes01.ts(14,5): error TS2322: Type '{ fooProp: string; } & Bar' is not assignable to type 'FooBar'. + Types of property 'fooProp' are incompatible. + Type 'string' is not assignable to type 'boolean'. + + +==== tests/cases/compiler/errorMessagesIntersectionTypes01.ts (1 errors) ==== + interface Foo { + fooProp: boolean; + } + + interface Bar { + barProp: string; + } + + interface FooBar extends Foo, Bar { + } + + declare function mixBar(obj: T): T & Bar; + + let fooBar: FooBar = mixBar({ + ~~~~~~ +!!! error TS2322: Type '{ fooProp: string; } & Bar' is not assignable to type 'FooBar'. +!!! error TS2322: Types of property 'fooProp' are incompatible. +!!! error TS2322: Type 'string' is not assignable to type 'boolean'. + fooProp: "frizzlebizzle" + }); \ No newline at end of file diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes01.js b/tests/baselines/reference/errorMessagesIntersectionTypes01.js new file mode 100644 index 00000000000..d0cd23ac53d --- /dev/null +++ b/tests/baselines/reference/errorMessagesIntersectionTypes01.js @@ -0,0 +1,22 @@ +//// [errorMessagesIntersectionTypes01.ts] +interface Foo { + fooProp: boolean; +} + +interface Bar { + barProp: string; +} + +interface FooBar extends Foo, Bar { +} + +declare function mixBar(obj: T): T & Bar; + +let fooBar: FooBar = mixBar({ + fooProp: "frizzlebizzle" +}); + +//// [errorMessagesIntersectionTypes01.js] +var fooBar = mixBar({ + fooProp: "frizzlebizzle" +}); diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes02.errors.txt b/tests/baselines/reference/errorMessagesIntersectionTypes02.errors.txt new file mode 100644 index 00000000000..06e06c87015 --- /dev/null +++ b/tests/baselines/reference/errorMessagesIntersectionTypes02.errors.txt @@ -0,0 +1,28 @@ +tests/cases/compiler/errorMessagesIntersectionTypes02.ts(14,5): error TS2322: Type '{ fooProp: string; } & Bar' is not assignable to type 'FooBar'. + Types of property 'fooProp' are incompatible. + Type 'string' is not assignable to type '"hello" | "world"'. + Type 'string' is not assignable to type '"world"'. + + +==== tests/cases/compiler/errorMessagesIntersectionTypes02.ts (1 errors) ==== + interface Foo { + fooProp: "hello" | "world"; + } + + interface Bar { + barProp: string; + } + + interface FooBar extends Foo, Bar { + } + + declare function mixBar(obj: T): T & Bar; + + let fooBar: FooBar = mixBar({ + ~~~~~~ +!!! error TS2322: Type '{ fooProp: string; } & Bar' is not assignable to type 'FooBar'. +!!! error TS2322: Types of property 'fooProp' are incompatible. +!!! error TS2322: Type 'string' is not assignable to type '"hello" | "world"'. +!!! error TS2322: Type 'string' is not assignable to type '"world"'. + fooProp: "frizzlebizzle" + }); \ No newline at end of file diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes02.js b/tests/baselines/reference/errorMessagesIntersectionTypes02.js new file mode 100644 index 00000000000..c5c24031877 --- /dev/null +++ b/tests/baselines/reference/errorMessagesIntersectionTypes02.js @@ -0,0 +1,22 @@ +//// [errorMessagesIntersectionTypes02.ts] +interface Foo { + fooProp: "hello" | "world"; +} + +interface Bar { + barProp: string; +} + +interface FooBar extends Foo, Bar { +} + +declare function mixBar(obj: T): T & Bar; + +let fooBar: FooBar = mixBar({ + fooProp: "frizzlebizzle" +}); + +//// [errorMessagesIntersectionTypes02.js] +var fooBar = mixBar({ + fooProp: "frizzlebizzle" +}); diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes03.errors.txt b/tests/baselines/reference/errorMessagesIntersectionTypes03.errors.txt new file mode 100644 index 00000000000..e37ff82aa7b --- /dev/null +++ b/tests/baselines/reference/errorMessagesIntersectionTypes03.errors.txt @@ -0,0 +1,42 @@ +tests/cases/compiler/errorMessagesIntersectionTypes03.ts(17,5): error TS2322: Type 'A & B' is not assignable to type 'T'. +tests/cases/compiler/errorMessagesIntersectionTypes03.ts(18,5): error TS2322: Type 'A & B' is not assignable to type 'U'. +tests/cases/compiler/errorMessagesIntersectionTypes03.ts(19,5): error TS2322: Type 'A & B' is not assignable to type 'V'. +tests/cases/compiler/errorMessagesIntersectionTypes03.ts(22,5): error TS2322: Type 'T & B' is not assignable to type 'U'. +tests/cases/compiler/errorMessagesIntersectionTypes03.ts(23,5): error TS2322: Type 'T & B' is not assignable to type 'V'. + + +==== tests/cases/compiler/errorMessagesIntersectionTypes03.ts (5 errors) ==== + interface A { + a; + } + + interface B { + b; + } + + function f(): void { + let t: T; + let u: U; + let v: V; + + let a_and_b: A & B; + let t_and_b: T & B; + + t = a_and_b; + ~ +!!! error TS2322: Type 'A & B' is not assignable to type 'T'. + u = a_and_b; + ~ +!!! error TS2322: Type 'A & B' is not assignable to type 'U'. + v = a_and_b; + ~ +!!! error TS2322: Type 'A & B' is not assignable to type 'V'. + + t = t_and_b; + u = t_and_b; + ~ +!!! error TS2322: Type 'T & B' is not assignable to type 'U'. + v = t_and_b; + ~ +!!! error TS2322: Type 'T & B' is not assignable to type 'V'. + } \ No newline at end of file diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes03.js b/tests/baselines/reference/errorMessagesIntersectionTypes03.js new file mode 100644 index 00000000000..278b81a329a --- /dev/null +++ b/tests/baselines/reference/errorMessagesIntersectionTypes03.js @@ -0,0 +1,40 @@ +//// [errorMessagesIntersectionTypes03.ts] +interface A { + a; +} + +interface B { + b; +} + +function f(): void { + let t: T; + let u: U; + let v: V; + + let a_and_b: A & B; + let t_and_b: T & B; + + t = a_and_b; + u = a_and_b; + v = a_and_b; + + t = t_and_b; + u = t_and_b; + v = t_and_b; +} + +//// [errorMessagesIntersectionTypes03.js] +function f() { + var t; + var u; + var v; + var a_and_b; + var t_and_b; + t = a_and_b; + u = a_and_b; + v = a_and_b; + t = t_and_b; + u = t_and_b; + v = t_and_b; +} diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt b/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt new file mode 100644 index 00000000000..7582c68ecec --- /dev/null +++ b/tests/baselines/reference/errorMessagesIntersectionTypes04.errors.txt @@ -0,0 +1,37 @@ +tests/cases/compiler/errorMessagesIntersectionTypes04.ts(17,5): error TS2322: Type 'A & B' is not assignable to type 'number'. +tests/cases/compiler/errorMessagesIntersectionTypes04.ts(18,5): error TS2322: Type 'A & B' is not assignable to type 'boolean'. +tests/cases/compiler/errorMessagesIntersectionTypes04.ts(19,5): error TS2322: Type 'A & B' is not assignable to type 'string'. +tests/cases/compiler/errorMessagesIntersectionTypes04.ts(21,5): error TS2322: Type 'number & boolean' is not assignable to type 'string'. + + +==== tests/cases/compiler/errorMessagesIntersectionTypes04.ts (4 errors) ==== + interface A { + a; + } + + interface B { + b; + } + + function f(): void { + let num: number; + let bool: boolean; + let str: string; + + let a_and_b: A & B; + let num_and_bool: number & boolean; + + num = a_and_b; + ~~~ +!!! error TS2322: Type 'A & B' is not assignable to type 'number'. + bool = a_and_b; + ~~~~ +!!! error TS2322: Type 'A & B' is not assignable to type 'boolean'. + str = a_and_b; + ~~~ +!!! error TS2322: Type 'A & B' is not assignable to type 'string'. + + str = num_and_bool; + ~~~ +!!! error TS2322: Type 'number & boolean' is not assignable to type 'string'. + } \ No newline at end of file diff --git a/tests/baselines/reference/errorMessagesIntersectionTypes04.js b/tests/baselines/reference/errorMessagesIntersectionTypes04.js new file mode 100644 index 00000000000..f663e1a7907 --- /dev/null +++ b/tests/baselines/reference/errorMessagesIntersectionTypes04.js @@ -0,0 +1,36 @@ +//// [errorMessagesIntersectionTypes04.ts] +interface A { + a; +} + +interface B { + b; +} + +function f(): void { + let num: number; + let bool: boolean; + let str: string; + + let a_and_b: A & B; + let num_and_bool: number & boolean; + + num = a_and_b; + bool = a_and_b; + str = a_and_b; + + str = num_and_bool; +} + +//// [errorMessagesIntersectionTypes04.js] +function f() { + var num; + var bool; + var str; + var a_and_b; + var num_and_bool; + num = a_and_b; + bool = a_and_b; + str = a_and_b; + str = num_and_bool; +} diff --git a/tests/baselines/reference/intersectionAndUnionTypes.errors.txt b/tests/baselines/reference/intersectionAndUnionTypes.errors.txt index d4526c815c4..5a04d4f25d3 100644 --- a/tests/baselines/reference/intersectionAndUnionTypes.errors.txt +++ b/tests/baselines/reference/intersectionAndUnionTypes.errors.txt @@ -17,27 +17,22 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(25,1): e tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(26,1): error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'A & B'. Type 'C & D' is not assignable to type 'A & B'. Type 'C & D' is not assignable to type 'A'. - Type 'D' is not assignable to type 'A'. - Property 'a' is missing in type 'D'. + Property 'a' is missing in type 'C & D'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(27,1): error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'A | B'. Type 'C & D' is not assignable to type 'A | B'. Type 'C & D' is not assignable to type 'B'. - Type 'D' is not assignable to type 'B'. - Property 'b' is missing in type 'D'. + Property 'b' is missing in type 'C & D'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(28,1): error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'C & D'. Type 'A & B' is not assignable to type 'C & D'. Type 'A & B' is not assignable to type 'C'. - Type 'B' is not assignable to type 'C'. - Property 'c' is missing in type 'B'. + Property 'c' is missing in type 'A & B'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(29,1): error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'C | D'. Type 'A & B' is not assignable to type 'C | D'. Type 'A & B' is not assignable to type 'D'. - Type 'B' is not assignable to type 'D'. - Property 'd' is missing in type 'B'. + Property 'd' is missing in type 'A & B'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(31,1): error TS2322: Type 'A & B' is not assignable to type '(A | B) & (C | D)'. Type 'A & B' is not assignable to type 'C | D'. Type 'A & B' is not assignable to type 'D'. - Type 'B' is not assignable to type 'D'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(32,1): error TS2322: Type 'A | B' is not assignable to type '(A | B) & (C | D)'. Type 'A' is not assignable to type '(A | B) & (C | D)'. Type 'A' is not assignable to type 'C | D'. @@ -46,7 +41,6 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(32,1): e tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(33,1): error TS2322: Type 'C & D' is not assignable to type '(A | B) & (C | D)'. Type 'C & D' is not assignable to type 'A | B'. Type 'C & D' is not assignable to type 'B'. - Type 'D' is not assignable to type 'B'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(34,1): error TS2322: Type 'C | D' is not assignable to type '(A | B) & (C | D)'. Type 'C' is not assignable to type '(A | B) & (C | D)'. Type 'C' is not assignable to type 'A | B'. @@ -54,14 +48,10 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(34,1): e Property 'b' is missing in type 'C'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(35,1): error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A & B'. Type '(A | B) & (C | D)' is not assignable to type 'A'. - Type 'C | D' is not assignable to type 'A'. - Type 'C' is not assignable to type 'A'. - Property 'a' is missing in type 'C'. + Property 'a' is missing in type '(A | B) & (C | D)'. tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C & D'. Type '(A | B) & (C | D)' is not assignable to type 'C'. - Type 'C | D' is not assignable to type 'C'. - Type 'D' is not assignable to type 'C'. - Property 'c' is missing in type 'D'. + Property 'c' is missing in type '(A | B) & (C | D)'. ==== tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts (14 errors) ==== @@ -115,36 +105,31 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): e !!! error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'A & B'. !!! error TS2322: Type 'C & D' is not assignable to type 'A & B'. !!! error TS2322: Type 'C & D' is not assignable to type 'A'. -!!! error TS2322: Type 'D' is not assignable to type 'A'. -!!! error TS2322: Property 'a' is missing in type 'D'. +!!! error TS2322: Property 'a' is missing in type 'C & D'. aob = x; ~~~ !!! error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'A | B'. !!! error TS2322: Type 'C & D' is not assignable to type 'A | B'. !!! error TS2322: Type 'C & D' is not assignable to type 'B'. -!!! error TS2322: Type 'D' is not assignable to type 'B'. -!!! error TS2322: Property 'b' is missing in type 'D'. +!!! error TS2322: Property 'b' is missing in type 'C & D'. cnd = x; ~~~ !!! error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'C & D'. !!! error TS2322: Type 'A & B' is not assignable to type 'C & D'. !!! error TS2322: Type 'A & B' is not assignable to type 'C'. -!!! error TS2322: Type 'B' is not assignable to type 'C'. -!!! error TS2322: Property 'c' is missing in type 'B'. +!!! error TS2322: Property 'c' is missing in type 'A & B'. cod = x; ~~~ !!! error TS2322: Type '(A & B) | (C & D)' is not assignable to type 'C | D'. !!! error TS2322: Type 'A & B' is not assignable to type 'C | D'. !!! error TS2322: Type 'A & B' is not assignable to type 'D'. -!!! error TS2322: Type 'B' is not assignable to type 'D'. -!!! error TS2322: Property 'd' is missing in type 'B'. +!!! error TS2322: Property 'd' is missing in type 'A & B'. y = anb; ~ !!! error TS2322: Type 'A & B' is not assignable to type '(A | B) & (C | D)'. !!! error TS2322: Type 'A & B' is not assignable to type 'C | D'. !!! error TS2322: Type 'A & B' is not assignable to type 'D'. -!!! error TS2322: Type 'B' is not assignable to type 'D'. y = aob; ~ !!! error TS2322: Type 'A | B' is not assignable to type '(A | B) & (C | D)'. @@ -157,7 +142,6 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): e !!! error TS2322: Type 'C & D' is not assignable to type '(A | B) & (C | D)'. !!! error TS2322: Type 'C & D' is not assignable to type 'A | B'. !!! error TS2322: Type 'C & D' is not assignable to type 'B'. -!!! error TS2322: Type 'D' is not assignable to type 'B'. y = cod; ~ !!! error TS2322: Type 'C | D' is not assignable to type '(A | B) & (C | D)'. @@ -169,16 +153,12 @@ tests/cases/conformance/types/intersection/intersectionAndUnionTypes.ts(37,1): e ~~~ !!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A & B'. !!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'A'. -!!! error TS2322: Type 'C | D' is not assignable to type 'A'. -!!! error TS2322: Type 'C' is not assignable to type 'A'. -!!! error TS2322: Property 'a' is missing in type 'C'. +!!! error TS2322: Property 'a' is missing in type '(A | B) & (C | D)'. aob = y; // Ok cnd = y; ~~~ !!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C & D'. !!! error TS2322: Type '(A | B) & (C | D)' is not assignable to type 'C'. -!!! error TS2322: Type 'C | D' is not assignable to type 'C'. -!!! error TS2322: Type 'D' is not assignable to type 'C'. -!!! error TS2322: Property 'c' is missing in type 'D'. +!!! error TS2322: Property 'c' is missing in type '(A | B) & (C | D)'. cod = y; // Ok \ No newline at end of file diff --git a/tests/baselines/reference/recursiveIntersectionTypes.errors.txt b/tests/baselines/reference/recursiveIntersectionTypes.errors.txt index bac97470e55..34a25c19712 100644 --- a/tests/baselines/reference/recursiveIntersectionTypes.errors.txt +++ b/tests/baselines/reference/recursiveIntersectionTypes.errors.txt @@ -1,7 +1,6 @@ tests/cases/conformance/types/intersection/recursiveIntersectionTypes.ts(19,1): error TS2322: Type 'Entity & { next: Entity & any; }' is not assignable to type 'Product & { next: Product & any; }'. Type 'Entity & { next: Entity & any; }' is not assignable to type 'Product'. - Type '{ next: Entity & any; }' is not assignable to type 'Product'. - Property 'price' is missing in type '{ next: Entity & any; }'. + Property 'price' is missing in type 'Entity & { next: Entity & any; }'. ==== tests/cases/conformance/types/intersection/recursiveIntersectionTypes.ts (1 errors) ==== @@ -27,6 +26,5 @@ tests/cases/conformance/types/intersection/recursiveIntersectionTypes.ts(19,1): ~~~~~~~~~~~ !!! error TS2322: Type 'Entity & { next: Entity & any; }' is not assignable to type 'Product & { next: Product & any; }'. !!! error TS2322: Type 'Entity & { next: Entity & any; }' is not assignable to type 'Product'. -!!! error TS2322: Type '{ next: Entity & any; }' is not assignable to type 'Product'. -!!! error TS2322: Property 'price' is missing in type '{ next: Entity & any; }'. +!!! error TS2322: Property 'price' is missing in type 'Entity & { next: Entity & any; }'. \ No newline at end of file diff --git a/tests/cases/compiler/errorMessagesIntersectionTypes01.ts b/tests/cases/compiler/errorMessagesIntersectionTypes01.ts new file mode 100644 index 00000000000..b5e375cb8a9 --- /dev/null +++ b/tests/cases/compiler/errorMessagesIntersectionTypes01.ts @@ -0,0 +1,16 @@ +interface Foo { + fooProp: boolean; +} + +interface Bar { + barProp: string; +} + +interface FooBar extends Foo, Bar { +} + +declare function mixBar(obj: T): T & Bar; + +let fooBar: FooBar = mixBar({ + fooProp: "frizzlebizzle" +}); \ No newline at end of file diff --git a/tests/cases/compiler/errorMessagesIntersectionTypes02.ts b/tests/cases/compiler/errorMessagesIntersectionTypes02.ts new file mode 100644 index 00000000000..3f21221e139 --- /dev/null +++ b/tests/cases/compiler/errorMessagesIntersectionTypes02.ts @@ -0,0 +1,16 @@ +interface Foo { + fooProp: "hello" | "world"; +} + +interface Bar { + barProp: string; +} + +interface FooBar extends Foo, Bar { +} + +declare function mixBar(obj: T): T & Bar; + +let fooBar: FooBar = mixBar({ + fooProp: "frizzlebizzle" +}); \ No newline at end of file diff --git a/tests/cases/compiler/errorMessagesIntersectionTypes03.ts b/tests/cases/compiler/errorMessagesIntersectionTypes03.ts new file mode 100644 index 00000000000..73969bc17b3 --- /dev/null +++ b/tests/cases/compiler/errorMessagesIntersectionTypes03.ts @@ -0,0 +1,24 @@ +interface A { + a; +} + +interface B { + b; +} + +function f(): void { + let t: T; + let u: U; + let v: V; + + let a_and_b: A & B; + let t_and_b: T & B; + + t = a_and_b; + u = a_and_b; + v = a_and_b; + + t = t_and_b; + u = t_and_b; + v = t_and_b; +} \ No newline at end of file diff --git a/tests/cases/compiler/errorMessagesIntersectionTypes04.ts b/tests/cases/compiler/errorMessagesIntersectionTypes04.ts new file mode 100644 index 00000000000..74dcfa658e7 --- /dev/null +++ b/tests/cases/compiler/errorMessagesIntersectionTypes04.ts @@ -0,0 +1,22 @@ +interface A { + a; +} + +interface B { + b; +} + +function f(): void { + let num: number; + let bool: boolean; + let str: string; + + let a_and_b: A & B; + let num_and_bool: number & boolean; + + num = a_and_b; + bool = a_and_b; + str = a_and_b; + + str = num_and_bool; +} \ No newline at end of file