Skip to content

Commit 2d8527f

Browse files
authored
Merge pull request #30779 from Microsoft/relateDiscriminants
Relate source types covered by a target discriminated union
2 parents de9b91f + 26fd6da commit 2d8527f

File tree

7 files changed

+1897
-70
lines changed

7 files changed

+1897
-70
lines changed

src/compiler/checker.ts

+204-70
Large diffs are not rendered by default.

src/compiler/core.ts

+25
Original file line numberDiff line numberDiff line change
@@ -2284,4 +2284,29 @@ namespace ts {
22842284
}
22852285
return result;
22862286
}
2287+
2288+
export function cartesianProduct<T>(arrays: readonly T[][]) {
2289+
const result: T[][] = [];
2290+
cartesianProductWorker(arrays, result, /*outer*/ undefined, 0);
2291+
return result;
2292+
}
2293+
2294+
function cartesianProductWorker<T>(arrays: readonly (readonly T[])[], result: (readonly T[])[], outer: readonly T[] | undefined, index: number) {
2295+
for (const element of arrays[index]) {
2296+
let inner: T[];
2297+
if (outer) {
2298+
inner = outer.slice();
2299+
inner.push(element);
2300+
}
2301+
else {
2302+
inner = [element];
2303+
}
2304+
if (index === arrays.length - 1) {
2305+
result.push(inner);
2306+
}
2307+
else {
2308+
cartesianProductWorker(arrays, result, inner, index + 1);
2309+
}
2310+
}
2311+
}
22872312
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
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: The maximum number of discriminant type 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 GH14865 {
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+
}
137+
138+
// https://github.com./Microsoft/TypeScript/issues/30170
139+
namespace GH30170 {
140+
interface Blue {
141+
color: 'blue'
142+
}
143+
interface Yellow {
144+
color?: 'yellow',
145+
}
146+
function draw(val: Blue | Yellow) { }
147+
148+
function drawWithColor(currentColor: 'blue' | 'yellow' | undefined) {
149+
return draw({ color: currentColor });
150+
}
151+
}
152+
153+
// https://github.com./Microsoft/TypeScript/issues/12052
154+
namespace GH12052 {
155+
interface ILinearAxis { type: "linear"; }
156+
157+
interface ICategoricalAxis { type: "categorical"; }
158+
159+
type IAxis = ILinearAxis | ICategoricalAxis;
160+
type IAxisType = "linear" | "categorical";
161+
162+
function getAxisType(): IAxisType {
163+
if (1 == 1) {
164+
return "categorical";
165+
} else {
166+
return "linear";
167+
}
168+
}
169+
170+
const bad: IAxis = { type: getAxisType() };
171+
const good: IAxis = { type: undefined };
172+
good.type = getAxisType();
173+
}
174+
175+
// https://github.com./Microsoft/TypeScript/issues/18421
176+
namespace GH18421 {
177+
interface ThingTypeOne {
178+
type: 'one';
179+
}
180+
181+
interface ThingTypeTwo {
182+
type: 'two';
183+
}
184+
185+
type ThingType = 'one' | 'two';
186+
187+
type Thing = ThingTypeOne | ThingTypeTwo;
188+
189+
function makeNewThing(thingType: ThingType): Thing {
190+
return {
191+
type: thingType
192+
};
193+
}
194+
}
195+
196+
// https://github.com./Microsoft/TypeScript/issues/15907
197+
namespace GH15907 {
198+
type Action = { type: 'activate' } | { type: 'disactivate' };
199+
200+
function dispatchAction(action: Action): void {
201+
202+
}
203+
204+
const active = true;
205+
206+
dispatchAction({ type : (active? 'disactivate' : 'activate') });
207+
}
208+
209+
// https://github.com./Microsoft/TypeScript/issues/20889
210+
namespace GH20889 {
211+
interface A1 {
212+
type: "A1";
213+
}
214+
interface A2 {
215+
type: "A2";
216+
}
217+
type AU = A1 | A2;
218+
219+
function foo(obj1: AU) {
220+
const obj2: AU = {
221+
type: obj1.type
222+
};
223+
}
224+
}

0 commit comments

Comments
 (0)