Skip to content

Commit 645853a

Browse files
committed
Relate a source type that is sufficiently covered by a target discriminated union
1 parent 0b3b4ea commit 645853a

7 files changed

+1283
-67
lines changed

src/compiler/checker.ts

+275-67
Large diffs are not rendered by default.

src/compiler/core.ts

+25
Original file line numberDiff line numberDiff line change
@@ -2317,4 +2317,29 @@ namespace ts {
23172317
}
23182318
return result;
23192319
}
2320+
2321+
export function cartesianProduct<T>(arrays: readonly T[][]) {
2322+
const result: T[][] = [];
2323+
cartesianProductWorker(arrays, result, /*outer*/ undefined, 0);
2324+
return result;
2325+
}
2326+
2327+
function cartesianProductWorker<T>(arrays: readonly (readonly T[])[], result: (readonly T[])[], outer: readonly T[] | undefined, index: number) {
2328+
for (const element of arrays[index]) {
2329+
let inner: T[];
2330+
if (outer) {
2331+
inner = outer.slice();
2332+
inner.push(element);
2333+
}
2334+
else {
2335+
inner = [element];
2336+
}
2337+
if (index === arrays.length - 1) {
2338+
result.push(inner);
2339+
}
2340+
else {
2341+
cartesianProductWorker(arrays, result, inner, index + 1);
2342+
}
2343+
}
2344+
}
23202345
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(44,5): error TS2322: Type 'S' is not assignable to type 'T'.
2+
Type 'S' is not assignable to type '{ a: 2; b: 3; }'.
3+
Types of property 'a' are incompatible.
4+
Type '0 | 2' is not assignable to type '2'.
5+
Type '0' is not assignable to type '2'.
6+
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(58,5): error TS2322: Type 'S' is not assignable to type 'T'.
7+
Property 'c' is missing in type 'S' but required in type '{ a: 2; b: 4 | 3; c: string; }'.
8+
tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts(82,5): error TS2322: Type 'S' is not assignable to type 'T'.
9+
Type 'S' is not assignable to type '{ a: 0 | 2 | 1; b: 0 | 2 | 1; c: 2; }'.
10+
Types of property 'c' are incompatible.
11+
Type '0 | 2 | 1' is not assignable to type '2'.
12+
Type '0' is not assignable to type '2'.
13+
14+
15+
==== tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts (3 errors) ====
16+
// see 'typeRelatedToDiscriminatedType' in checker.ts:
17+
18+
// IteratorResult
19+
namespace Example1 {
20+
type S = { done: boolean, value: number };
21+
type T =
22+
| { done: true, value: number } // T0
23+
| { done: false, value: number }; // T1
24+
25+
declare let s: S;
26+
declare let t: T;
27+
28+
// S is assignable to T0 when S["done"] is true
29+
// S is assignable to T1 when S["done"] is false
30+
t = s;
31+
}
32+
33+
// Dropping constituents of T
34+
namespace Example2 {
35+
type S = { a: 0 | 2, b: 4 };
36+
type T = { a: 0, b: 1 | 4 } // T0
37+
| { a: 1, b: 2 } // T1
38+
| { a: 2, b: 3 | 4 }; // T2
39+
declare let s: S;
40+
declare let t: T;
41+
42+
// S is assignable to T0 when S["a"] is 0
43+
// S is assignable to T2 when S["a"] is 2
44+
t = s;
45+
}
46+
47+
// Unmatched discriminants
48+
namespace Example3 {
49+
type S = { a: 0 | 2, b: 4 };
50+
type T = { a: 0, b: 1 | 4 } // T0
51+
| { a: 1, b: 2 | 4 } // T1
52+
| { a: 2, b: 3 }; // T2
53+
declare let s: S;
54+
declare let t: T;
55+
56+
// S is assignable to T0 when S["a"] is 0
57+
// S is *not* assignable to T1 when S["b"] is 4
58+
// S is *not* assignable to T2 when S["a"] is 2
59+
t = s;
60+
~
61+
!!! error TS2322: Type 'S' is not assignable to type 'T'.
62+
!!! error TS2322: Type 'S' is not assignable to type '{ a: 2; b: 3; }'.
63+
!!! error TS2322: Types of property 'a' are incompatible.
64+
!!! error TS2322: Type '0 | 2' is not assignable to type '2'.
65+
!!! error TS2322: Type '0' is not assignable to type '2'.
66+
}
67+
68+
// Unmatched non-discriminants
69+
namespace Example4 {
70+
type S = { a: 0 | 2, b: 4 };
71+
type T = { a: 0, b: 1 | 4 } // T0
72+
| { a: 1, b: 2 } // T1
73+
| { a: 2, b: 3 | 4, c: string }; // T2
74+
declare let s: S;
75+
declare let t: T;
76+
77+
// S is assignable to T0 when S["a"] is 0
78+
// S is *not* assignable to T2 when S["a"] is 2 as S is missing "c"
79+
t = s;
80+
~
81+
!!! error TS2322: Type 'S' is not assignable to type 'T'.
82+
!!! error TS2322: Property 'c' is missing in type 'S' but required in type '{ a: 2; b: 4 | 3; c: string; }'.
83+
!!! related TS2728 tests/cases/conformance/types/typeRelationships/assignmentCompatibility/assignmentCompatWithDiscriminatedUnion.ts:52:36: 'c' is declared here.
84+
}
85+
86+
// Maximum discriminant combinations
87+
namespace Example5 {
88+
// NOTE: MAX_DISCRIMINANT_COMBINATIONS is currently 25
89+
// 3 discriminant properties with 3 types a piece
90+
// is 27 possible combinations.
91+
type N = 0 | 1 | 2;
92+
type S = { a: N, b: N, c: N };
93+
type T = { a: 0, b: N, c: N }
94+
| { a: 1, b: N, c: N }
95+
| { a: 2, b: N, c: N }
96+
| { a: N, b: 0, c: N }
97+
| { a: N, b: 1, c: N }
98+
| { a: N, b: 2, c: N }
99+
| { a: N, b: N, c: 0 }
100+
| { a: N, b: N, c: 1 }
101+
| { a: N, b: N, c: 2 };
102+
declare let s: S;
103+
declare let t: T;
104+
105+
// S *should* be assignable but the number of
106+
// combinations is too complex.
107+
t = s;
108+
~
109+
!!! error TS2322: Type 'S' is not assignable to type 'T'.
110+
!!! error TS2322: Type 'S' is not assignable to type '{ a: 0 | 2 | 1; b: 0 | 2 | 1; c: 2; }'.
111+
!!! error TS2322: Types of property 'c' are incompatible.
112+
!!! error TS2322: Type '0 | 2 | 1' is not assignable to type '2'.
113+
!!! error TS2322: Type '0' is not assignable to type '2'.
114+
}
115+
116+
// https://github.com/Microsoft/TypeScript/issues/14865
117+
namespace Example6 {
118+
type Style1 = {
119+
type: "A";
120+
data: string;
121+
} | {
122+
type: "B";
123+
data: string;
124+
};
125+
126+
type Style2 = {
127+
type: "A" | "B";
128+
data: string;
129+
}
130+
131+
const a: Style2 = { type: "A", data: "whatevs" };
132+
let b: Style1;
133+
a.type; // "A" | "B"
134+
b.type; // "A" | "B"
135+
b = a; // should be assignable
136+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//// [assignmentCompatWithDiscriminatedUnion.ts]
2+
// see 'typeRelatedToDiscriminatedType' in checker.ts:
3+
4+
// IteratorResult
5+
namespace Example1 {
6+
type S = { done: boolean, value: number };
7+
type T =
8+
| { done: true, value: number } // T0
9+
| { done: false, value: number }; // T1
10+
11+
declare let s: S;
12+
declare let t: T;
13+
14+
// S is assignable to T0 when S["done"] is true
15+
// S is assignable to T1 when S["done"] is false
16+
t = s;
17+
}
18+
19+
// Dropping constituents of T
20+
namespace Example2 {
21+
type S = { a: 0 | 2, b: 4 };
22+
type T = { a: 0, b: 1 | 4 } // T0
23+
| { a: 1, b: 2 } // T1
24+
| { a: 2, b: 3 | 4 }; // T2
25+
declare let s: S;
26+
declare let t: T;
27+
28+
// S is assignable to T0 when S["a"] is 0
29+
// S is assignable to T2 when S["a"] is 2
30+
t = s;
31+
}
32+
33+
// Unmatched discriminants
34+
namespace Example3 {
35+
type S = { a: 0 | 2, b: 4 };
36+
type T = { a: 0, b: 1 | 4 } // T0
37+
| { a: 1, b: 2 | 4 } // T1
38+
| { a: 2, b: 3 }; // T2
39+
declare let s: S;
40+
declare let t: T;
41+
42+
// S is assignable to T0 when S["a"] is 0
43+
// S is *not* assignable to T1 when S["b"] is 4
44+
// S is *not* assignable to T2 when S["a"] is 2
45+
t = s;
46+
}
47+
48+
// Unmatched non-discriminants
49+
namespace Example4 {
50+
type S = { a: 0 | 2, b: 4 };
51+
type T = { a: 0, b: 1 | 4 } // T0
52+
| { a: 1, b: 2 } // T1
53+
| { a: 2, b: 3 | 4, c: string }; // T2
54+
declare let s: S;
55+
declare let t: T;
56+
57+
// S is assignable to T0 when S["a"] is 0
58+
// S is *not* assignable to T2 when S["a"] is 2 as S is missing "c"
59+
t = s;
60+
}
61+
62+
// Maximum discriminant combinations
63+
namespace Example5 {
64+
// NOTE: MAX_DISCRIMINANT_COMBINATIONS is currently 25
65+
// 3 discriminant properties with 3 types a piece
66+
// is 27 possible combinations.
67+
type N = 0 | 1 | 2;
68+
type S = { a: N, b: N, c: N };
69+
type T = { a: 0, b: N, c: N }
70+
| { a: 1, b: N, c: N }
71+
| { a: 2, b: N, c: N }
72+
| { a: N, b: 0, c: N }
73+
| { a: N, b: 1, c: N }
74+
| { a: N, b: 2, c: N }
75+
| { a: N, b: N, c: 0 }
76+
| { a: N, b: N, c: 1 }
77+
| { a: N, b: N, c: 2 };
78+
declare let s: S;
79+
declare let t: T;
80+
81+
// S *should* be assignable but the number of
82+
// combinations is too complex.
83+
t = s;
84+
}
85+
86+
// https://github.com/Microsoft/TypeScript/issues/14865
87+
namespace Example6 {
88+
type Style1 = {
89+
type: "A";
90+
data: string;
91+
} | {
92+
type: "B";
93+
data: string;
94+
};
95+
96+
type Style2 = {
97+
type: "A" | "B";
98+
data: string;
99+
}
100+
101+
const a: Style2 = { type: "A", data: "whatevs" };
102+
let b: Style1;
103+
a.type; // "A" | "B"
104+
b.type; // "A" | "B"
105+
b = a; // should be assignable
106+
}
107+
108+
//// [assignmentCompatWithDiscriminatedUnion.js]
109+
// see 'typeRelatedToDiscriminatedType' in checker.ts:
110+
// IteratorResult
111+
var Example1;
112+
(function (Example1) {
113+
// S is assignable to T0 when S["done"] is true
114+
// S is assignable to T1 when S["done"] is false
115+
t = s;
116+
})(Example1 || (Example1 = {}));
117+
// Dropping constituents of T
118+
var Example2;
119+
(function (Example2) {
120+
// S is assignable to T0 when S["a"] is 0
121+
// S is assignable to T2 when S["a"] is 2
122+
t = s;
123+
})(Example2 || (Example2 = {}));
124+
// Unmatched discriminants
125+
var Example3;
126+
(function (Example3) {
127+
// S is assignable to T0 when S["a"] is 0
128+
// S is *not* assignable to T1 when S["b"] is 4
129+
// S is *not* assignable to T2 when S["a"] is 2
130+
t = s;
131+
})(Example3 || (Example3 = {}));
132+
// Unmatched non-discriminants
133+
var Example4;
134+
(function (Example4) {
135+
// S is assignable to T0 when S["a"] is 0
136+
// S is *not* assignable to T2 when S["a"] is 2 as S is missing "c"
137+
t = s;
138+
})(Example4 || (Example4 = {}));
139+
// Maximum discriminant combinations
140+
var Example5;
141+
(function (Example5) {
142+
// S *should* be assignable but the number of
143+
// combinations is too complex.
144+
t = s;
145+
})(Example5 || (Example5 = {}));
146+
// https://github.com/Microsoft/TypeScript/issues/14865
147+
var Example6;
148+
(function (Example6) {
149+
var a = { type: "A", data: "whatevs" };
150+
var b;
151+
a.type; // "A" | "B"
152+
b.type; // "A" | "B"
153+
b = a; // should be assignable
154+
})(Example6 || (Example6 = {}));

0 commit comments

Comments
 (0)