Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 2d8527f

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

7 files changed

+1,897-70Lines changed: 1897 additions & 70 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/compiler/checker.ts‎

Copy file name to clipboardExpand all lines: src/compiler/checker.ts
+204-70Lines changed: 204 additions & 70 deletions
Large diffs are not rendered by default.
Collapse file

‎src/compiler/core.ts‎

Copy file name to clipboardExpand all lines: src/compiler/core.ts
+25Lines changed: 25 additions & 0 deletions
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
}
Collapse file
+224Lines changed: 224 additions & 0 deletions
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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.