Skip to content

Commit 6627d33

Browse files
Merge pull request #27087 from Microsoft/overlappyTypes
Elaborate on types in unions with the most overlap in properties
2 parents 3e4c5c9 + 5a4378e commit 6627d33

16 files changed

+368
-47
lines changed

src/compiler/checker.ts

+30-1
Original file line numberDiff line numberDiff line change
@@ -11920,7 +11920,8 @@ namespace ts {
1192011920
findMatchingDiscriminantType(source, target) ||
1192111921
findMatchingTypeReferenceOrTypeAliasReference(source, target) ||
1192211922
findBestTypeForObjectLiteral(source, target) ||
11923-
findBestTypeForInvokable(source, target);
11923+
findBestTypeForInvokable(source, target) ||
11924+
findMostOverlappyType(source, target);
1192411925

1192511926
isRelatedTo(source, bestMatchingType || targetTypes[targetTypes.length - 1], /*reportErrors*/ true);
1192611927
}
@@ -11960,6 +11961,34 @@ namespace ts {
1196011961
}
1196111962
}
1196211963

11964+
function findMostOverlappyType(source: Type, unionTarget: UnionOrIntersectionType) {
11965+
let bestMatch: Type | undefined;
11966+
let matchingCount = 0;
11967+
for (const target of unionTarget.types) {
11968+
const overlap = getIntersectionType([getIndexType(source), getIndexType(target)]);
11969+
if (overlap.flags & TypeFlags.Index) {
11970+
// perfect overlap of keys
11971+
bestMatch = target;
11972+
matchingCount = Infinity;
11973+
}
11974+
else if (overlap.flags & TypeFlags.Union) {
11975+
// Some subset overlap if we have only string literals.
11976+
// If we have a union of index types, it seems likely that we
11977+
// needed to elaborate between two generic mapped types anyway.
11978+
const len = length((overlap as UnionType).types);
11979+
if (len >= matchingCount) {
11980+
bestMatch = target;
11981+
matchingCount = len;
11982+
}
11983+
}
11984+
else if (!(overlap.flags & TypeFlags.Never) && 1 >= matchingCount) {
11985+
bestMatch = target;
11986+
matchingCount = 1;
11987+
}
11988+
}
11989+
return bestMatch;
11990+
}
11991+
1196311992
// Keep this up-to-date with the same logic within `getApparentTypeOfContextualType`, since they should behave similarly
1196411993
function findMatchingDiscriminantType(source: Type, target: Type) {
1196511994
if (target.flags & TypeFlags.Union) {

tests/baselines/reference/checkJsxChildrenProperty2.errors.txt

+8-8
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,19 @@ tests/cases/conformance/jsx/file.tsx(17,11): error TS2710: 'children' are specif
33
tests/cases/conformance/jsx/file.tsx(31,6): error TS2322: Type '{ children: (Element | ((name: string) => Element))[]; a: number; b: string; }' is not assignable to type 'Prop'.
44
Types of property 'children' are incompatible.
55
Type '(Element | ((name: string) => Element))[]' is not assignable to type 'string | Element'.
6-
Type '(Element | ((name: string) => Element))[]' is missing the following properties from type 'Element': type, props
6+
Type '(Element | ((name: string) => Element))[]' is not assignable to type 'string'.
77
tests/cases/conformance/jsx/file.tsx(37,6): error TS2322: Type '{ children: (number | Element)[]; a: number; b: string; }' is not assignable to type 'Prop'.
88
Types of property 'children' are incompatible.
99
Type '(number | Element)[]' is not assignable to type 'string | Element'.
10-
Type '(number | Element)[]' is missing the following properties from type 'Element': type, props
10+
Type '(number | Element)[]' is not assignable to type 'string'.
1111
tests/cases/conformance/jsx/file.tsx(43,6): error TS2322: Type '{ children: (string | Element)[]; a: number; b: string; }' is not assignable to type 'Prop'.
1212
Types of property 'children' are incompatible.
1313
Type '(string | Element)[]' is not assignable to type 'string | Element'.
14-
Type '(string | Element)[]' is missing the following properties from type 'Element': type, props
14+
Type '(string | Element)[]' is not assignable to type 'string'.
1515
tests/cases/conformance/jsx/file.tsx(49,6): error TS2322: Type '{ children: Element[]; a: number; b: string; }' is not assignable to type 'Prop'.
1616
Types of property 'children' are incompatible.
1717
Type 'Element[]' is not assignable to type 'string | Element'.
18-
Type 'Element[]' is missing the following properties from type 'Element': type, props
18+
Type 'Element[]' is not assignable to type 'string'.
1919

2020

2121
==== tests/cases/conformance/jsx/file.tsx (6 errors) ====
@@ -59,7 +59,7 @@ tests/cases/conformance/jsx/file.tsx(49,6): error TS2322: Type '{ children: Elem
5959
!!! error TS2322: Type '{ children: (Element | ((name: string) => Element))[]; a: number; b: string; }' is not assignable to type 'Prop'.
6060
!!! error TS2322: Types of property 'children' are incompatible.
6161
!!! error TS2322: Type '(Element | ((name: string) => Element))[]' is not assignable to type 'string | Element'.
62-
!!! error TS2322: Type '(Element | ((name: string) => Element))[]' is missing the following properties from type 'Element': type, props
62+
!!! error TS2322: Type '(Element | ((name: string) => Element))[]' is not assignable to type 'string'.
6363
<div> My Div </div>
6464
{(name: string) => <div> My name {name} </div>}
6565
</Comp>;
@@ -70,7 +70,7 @@ tests/cases/conformance/jsx/file.tsx(49,6): error TS2322: Type '{ children: Elem
7070
!!! error TS2322: Type '{ children: (number | Element)[]; a: number; b: string; }' is not assignable to type 'Prop'.
7171
!!! error TS2322: Types of property 'children' are incompatible.
7272
!!! error TS2322: Type '(number | Element)[]' is not assignable to type 'string | Element'.
73-
!!! error TS2322: Type '(number | Element)[]' is missing the following properties from type 'Element': type, props
73+
!!! error TS2322: Type '(number | Element)[]' is not assignable to type 'string'.
7474
<div> My Div </div>
7575
{1000000}
7676
</Comp>;
@@ -81,7 +81,7 @@ tests/cases/conformance/jsx/file.tsx(49,6): error TS2322: Type '{ children: Elem
8181
!!! error TS2322: Type '{ children: (string | Element)[]; a: number; b: string; }' is not assignable to type 'Prop'.
8282
!!! error TS2322: Types of property 'children' are incompatible.
8383
!!! error TS2322: Type '(string | Element)[]' is not assignable to type 'string | Element'.
84-
!!! error TS2322: Type '(string | Element)[]' is missing the following properties from type 'Element': type, props
84+
!!! error TS2322: Type '(string | Element)[]' is not assignable to type 'string'.
8585
<div> My Div </div>
8686
hi hi hi!
8787
</Comp>;
@@ -92,7 +92,7 @@ tests/cases/conformance/jsx/file.tsx(49,6): error TS2322: Type '{ children: Elem
9292
!!! error TS2322: Type '{ children: Element[]; a: number; b: string; }' is not assignable to type 'Prop'.
9393
!!! error TS2322: Types of property 'children' are incompatible.
9494
!!! error TS2322: Type 'Element[]' is not assignable to type 'string | Element'.
95-
!!! error TS2322: Type 'Element[]' is missing the following properties from type 'Element': type, props
95+
!!! error TS2322: Type 'Element[]' is not assignable to type 'string'.
9696
<div> My Div </div>
9797
<div> My Div </div>
9898
</Comp>;

tests/baselines/reference/contextualTypeWithUnionTypeObjectLiteral.errors.txt

+14-9
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,15 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
99
Type 'string | number' is not assignable to type 'number'.
1010
Type 'string' is not assignable to type 'number'.
1111
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(21,5): error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; }'.
12-
Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: number; }'.
12+
Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; }'.
1313
Types of property 'prop' are incompatible.
14-
Type 'string | number' is not assignable to type 'number'.
15-
Type 'string' is not assignable to type 'number'.
14+
Type 'string | number' is not assignable to type 'string'.
15+
Type 'number' is not assignable to type 'string'.
1616
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(25,5): error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'.
17-
Property 'anotherP1' is missing in type '{ prop: string | number; anotherP: string; }' but required in type '{ prop: number; anotherP1: number; }'.
17+
Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; }'.
18+
Types of property 'prop' are incompatible.
19+
Type 'string | number' is not assignable to type 'string'.
20+
Type 'number' is not assignable to type 'string'.
1821
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(29,5): error TS2322: Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'.
1922
Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: number; anotherP1: number; }'.
2023
Types of property 'prop' are incompatible.
@@ -62,18 +65,20 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
6265
var objStrOrNum6: { prop: string; anotherP: string; } | { prop: number } = {
6366
~~~~~~~~~~~~
6467
!!! error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; }'.
65-
!!! error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: number; }'.
68+
!!! error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; }'.
6669
!!! error TS2322: Types of property 'prop' are incompatible.
67-
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
68-
!!! error TS2322: Type 'string' is not assignable to type 'number'.
70+
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
71+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
6972
prop: strOrNumber,
7073
anotherP: str
7174
};
7275
var objStrOrNum7: { prop: string; anotherP: string; } | { prop: number; anotherP1: number } = {
7376
~~~~~~~~~~~~
7477
!!! error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'.
75-
!!! error TS2322: Property 'anotherP1' is missing in type '{ prop: string | number; anotherP: string; }' but required in type '{ prop: number; anotherP1: number; }'.
76-
!!! related TS2728 tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts:25:73: 'anotherP1' is declared here.
78+
!!! error TS2322: Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: string; anotherP: string; }'.
79+
!!! error TS2322: Types of property 'prop' are incompatible.
80+
!!! error TS2322: Type 'string | number' is not assignable to type 'string'.
81+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
7782
prop: strOrNumber,
7883
anotherP: str
7984
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
tests/cases/compiler/errorsOnUnionsOfOverlappingObjects01.ts(18,3): error TS2345: Argument of type '{ a: string; b: string; }' is not assignable to parameter of type 'Foo | Other'.
2+
Type '{ a: string; b: string; }' is not assignable to type 'Foo'.
3+
Types of property 'b' are incompatible.
4+
Type 'string' is not assignable to type 'number'.
5+
tests/cases/compiler/errorsOnUnionsOfOverlappingObjects01.ts(19,3): error TS2345: Argument of type '{ a: string; b: string; }' is not assignable to parameter of type 'Foo | Other'.
6+
Type '{ a: string; b: string; }' is not assignable to type 'Foo'.
7+
Types of property 'b' are incompatible.
8+
Type 'string' is not assignable to type 'number'.
9+
tests/cases/compiler/errorsOnUnionsOfOverlappingObjects01.ts(24,5): error TS2345: Argument of type '{ a: string; b: string; }' is not assignable to parameter of type 'Bar | Other'.
10+
Object literal may only specify known properties, and 'a' does not exist in type 'Bar | Other'.
11+
12+
13+
==== tests/cases/compiler/errorsOnUnionsOfOverlappingObjects01.ts (3 errors) ====
14+
interface Foo {
15+
a: string;
16+
b: number;
17+
};
18+
19+
interface Bar {
20+
b: string;
21+
}
22+
23+
interface Other {
24+
totallyUnrelatedProperty: number;
25+
}
26+
27+
export let x = { a: '', b: '' };
28+
29+
declare function f(x: Foo | Other): any;
30+
31+
f(x);
32+
~
33+
!!! error TS2345: Argument of type '{ a: string; b: string; }' is not assignable to parameter of type 'Foo | Other'.
34+
!!! error TS2345: Type '{ a: string; b: string; }' is not assignable to type 'Foo'.
35+
!!! error TS2345: Types of property 'b' are incompatible.
36+
!!! error TS2345: Type 'string' is not assignable to type 'number'.
37+
f({ a: '', b: '' })
38+
~~~~~~~~~~~~~~~~
39+
!!! error TS2345: Argument of type '{ a: string; b: string; }' is not assignable to parameter of type 'Foo | Other'.
40+
!!! error TS2345: Type '{ a: string; b: string; }' is not assignable to type 'Foo'.
41+
!!! error TS2345: Types of property 'b' are incompatible.
42+
!!! error TS2345: Type 'string' is not assignable to type 'number'.
43+
44+
declare function g(x: Bar | Other): any;
45+
46+
g(x);
47+
g({ a: '', b: '' })
48+
~~~~~
49+
!!! error TS2345: Argument of type '{ a: string; b: string; }' is not assignable to parameter of type 'Bar | Other'.
50+
!!! error TS2345: Object literal may only specify known properties, and 'a' does not exist in type 'Bar | Other'.
51+
52+
declare function h(x: Foo | Bar | Other): any;
53+
54+
h(x);
55+
h({ a: '', b: '' })
56+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//// [errorsOnUnionsOfOverlappingObjects01.ts]
2+
interface Foo {
3+
a: string;
4+
b: number;
5+
};
6+
7+
interface Bar {
8+
b: string;
9+
}
10+
11+
interface Other {
12+
totallyUnrelatedProperty: number;
13+
}
14+
15+
export let x = { a: '', b: '' };
16+
17+
declare function f(x: Foo | Other): any;
18+
19+
f(x);
20+
f({ a: '', b: '' })
21+
22+
declare function g(x: Bar | Other): any;
23+
24+
g(x);
25+
g({ a: '', b: '' })
26+
27+
declare function h(x: Foo | Bar | Other): any;
28+
29+
h(x);
30+
h({ a: '', b: '' })
31+
32+
33+
//// [errorsOnUnionsOfOverlappingObjects01.js]
34+
"use strict";
35+
exports.__esModule = true;
36+
;
37+
exports.x = { a: '', b: '' };
38+
f(exports.x);
39+
f({ a: '', b: '' });
40+
g(exports.x);
41+
g({ a: '', b: '' });
42+
h(exports.x);
43+
h({ a: '', b: '' });
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
=== tests/cases/compiler/errorsOnUnionsOfOverlappingObjects01.ts ===
2+
interface Foo {
3+
>Foo : Symbol(Foo, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 0, 0))
4+
5+
a: string;
6+
>a : Symbol(Foo.a, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 0, 15))
7+
8+
b: number;
9+
>b : Symbol(Foo.b, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 1, 14))
10+
11+
};
12+
13+
interface Bar {
14+
>Bar : Symbol(Bar, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 3, 2))
15+
16+
b: string;
17+
>b : Symbol(Bar.b, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 5, 15))
18+
}
19+
20+
interface Other {
21+
>Other : Symbol(Other, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 7, 1))
22+
23+
totallyUnrelatedProperty: number;
24+
>totallyUnrelatedProperty : Symbol(Other.totallyUnrelatedProperty, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 9, 17))
25+
}
26+
27+
export let x = { a: '', b: '' };
28+
>x : Symbol(x, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 10))
29+
>a : Symbol(a, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 16))
30+
>b : Symbol(b, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 23))
31+
32+
declare function f(x: Foo | Other): any;
33+
>f : Symbol(f, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 32))
34+
>x : Symbol(x, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 15, 19))
35+
>Foo : Symbol(Foo, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 0, 0))
36+
>Other : Symbol(Other, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 7, 1))
37+
38+
f(x);
39+
>f : Symbol(f, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 32))
40+
>x : Symbol(x, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 10))
41+
42+
f({ a: '', b: '' })
43+
>f : Symbol(f, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 32))
44+
>a : Symbol(a, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 18, 3))
45+
>b : Symbol(b, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 18, 10))
46+
47+
declare function g(x: Bar | Other): any;
48+
>g : Symbol(g, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 18, 19))
49+
>x : Symbol(x, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 20, 19))
50+
>Bar : Symbol(Bar, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 3, 2))
51+
>Other : Symbol(Other, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 7, 1))
52+
53+
g(x);
54+
>g : Symbol(g, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 18, 19))
55+
>x : Symbol(x, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 10))
56+
57+
g({ a: '', b: '' })
58+
>g : Symbol(g, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 18, 19))
59+
>a : Symbol(a, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 23, 3))
60+
>b : Symbol(b, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 23, 10))
61+
62+
declare function h(x: Foo | Bar | Other): any;
63+
>h : Symbol(h, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 23, 19))
64+
>x : Symbol(x, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 25, 19))
65+
>Foo : Symbol(Foo, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 0, 0))
66+
>Bar : Symbol(Bar, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 3, 2))
67+
>Other : Symbol(Other, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 7, 1))
68+
69+
h(x);
70+
>h : Symbol(h, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 23, 19))
71+
>x : Symbol(x, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 13, 10))
72+
73+
h({ a: '', b: '' })
74+
>h : Symbol(h, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 23, 19))
75+
>a : Symbol(a, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 28, 3))
76+
>b : Symbol(b, Decl(errorsOnUnionsOfOverlappingObjects01.ts, 28, 10))
77+

0 commit comments

Comments
 (0)