Skip to content

Commit 7b96c28

Browse files
authored
Add Support for Using Aliased Discriminants in Conditional Statements (#56173)
1 parent 50f4884 commit 7b96c28

File tree

6 files changed

+1126
-0
lines changed

6 files changed

+1126
-0
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27244,6 +27244,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2724427244
case SyntaxKind.ElementAccessExpression:
2724527245
// The resolvedSymbol property is initialized by checkPropertyAccess or checkElementAccess before we get here.
2724627246
return isConstantReference((node as AccessExpression).expression) && isReadonlySymbol(getNodeLinks(node).resolvedSymbol || unknownSymbol);
27247+
case SyntaxKind.ObjectBindingPattern:
27248+
case SyntaxKind.ArrayBindingPattern:
27249+
const rootDeclaration = getRootDeclaration(node.parent);
27250+
return isVariableDeclaration(rootDeclaration) && isVarConstLike(rootDeclaration);
2724727251
}
2724827252
return false;
2724927253
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
controlFlowAliasedDiscriminants.ts(39,9): error TS18048: 'data1' is possibly 'undefined'.
2+
controlFlowAliasedDiscriminants.ts(40,9): error TS18048: 'data2' is possibly 'undefined'.
3+
controlFlowAliasedDiscriminants.ts(65,9): error TS18048: 'bar2' is possibly 'undefined'.
4+
controlFlowAliasedDiscriminants.ts(66,9): error TS18048: 'bar3' is possibly 'undefined'.
5+
controlFlowAliasedDiscriminants.ts(86,14): error TS1360: Type 'string | number' does not satisfy the expected type 'string'.
6+
Type 'number' is not assignable to type 'string'.
7+
controlFlowAliasedDiscriminants.ts(98,19): error TS1360: Type 'string | number' does not satisfy the expected type 'string'.
8+
Type 'number' is not assignable to type 'string'.
9+
10+
11+
==== controlFlowAliasedDiscriminants.ts (6 errors) ====
12+
type UseQueryResult<T> = {
13+
isSuccess: false;
14+
data: undefined;
15+
} | {
16+
isSuccess: true;
17+
data: T
18+
};
19+
20+
function useQuery(): UseQueryResult<number> {
21+
return {
22+
isSuccess: false,
23+
data: undefined,
24+
};
25+
}
26+
27+
const { data: data1, isSuccess: isSuccess1 } = useQuery();
28+
const { data: data2, isSuccess: isSuccess2 } = useQuery();
29+
const { data: data3, isSuccess: isSuccess3 } = useQuery();
30+
31+
if (isSuccess1 && isSuccess2 && isSuccess3) {
32+
data1.toExponential(); // should ok
33+
data2.toExponential(); // should ok
34+
data3.toExponential(); // should ok
35+
}
36+
37+
const areSuccess = isSuccess1 && isSuccess2 && isSuccess3;
38+
if (areSuccess) {
39+
data1.toExponential(); // should ok
40+
data2.toExponential(); // should ok
41+
data3.toExponential(); // should ok
42+
}
43+
44+
{
45+
let { data: data1, isSuccess: isSuccess1 } = useQuery();
46+
let { data: data2, isSuccess: isSuccess2 } = useQuery();
47+
const { data: data3, isSuccess: isSuccess3 } = useQuery();
48+
const areSuccess = isSuccess1 && isSuccess2 && isSuccess3;
49+
if (areSuccess) {
50+
data1.toExponential(); // should error
51+
~~~~~
52+
!!! error TS18048: 'data1' is possibly 'undefined'.
53+
data2.toExponential(); // should error
54+
~~~~~
55+
!!! error TS18048: 'data2' is possibly 'undefined'.
56+
data3.toExponential(); // should ok
57+
}
58+
}
59+
60+
declare function getArrayResult(): [true, number] | [false, undefined];
61+
{
62+
const [foo1, bar1] = getArrayResult();
63+
const [foo2, bar2] = getArrayResult();
64+
const [foo3, bar3] = getArrayResult();
65+
const arrayAllSuccess = foo1 && foo2 && foo3;
66+
if (arrayAllSuccess) {
67+
bar1.toExponential(); // should ok
68+
bar2.toExponential(); // should ok
69+
bar3.toExponential(); // should ok
70+
}
71+
}
72+
73+
{
74+
const [foo1, bar1] = getArrayResult();
75+
let [foo2, bar2] = getArrayResult();
76+
let [foo3, bar3] = getArrayResult();
77+
const arrayAllSuccess = foo1 && foo2 && foo3;
78+
if (arrayAllSuccess) {
79+
bar1.toExponential(); // should ok
80+
bar2.toExponential(); // should error
81+
~~~~
82+
!!! error TS18048: 'bar2' is possibly 'undefined'.
83+
bar3.toExponential(); // should error
84+
~~~~
85+
!!! error TS18048: 'bar3' is possibly 'undefined'.
86+
}
87+
}
88+
89+
type Nested = {
90+
type: 'string';
91+
resp: {
92+
data: string
93+
}
94+
} | {
95+
type: 'number';
96+
resp: {
97+
data: number;
98+
}
99+
}
100+
101+
{
102+
let resp!: Nested;
103+
const { resp: { data }, type } = resp;
104+
if (type === 'string') {
105+
data satisfies string;
106+
~~~~~~~~~
107+
!!! error TS1360: Type 'string | number' does not satisfy the expected type 'string'.
108+
!!! error TS1360: Type 'number' is not assignable to type 'string'.
109+
}
110+
if (resp.type === 'string') {
111+
resp.resp.data satisfies string;
112+
}
113+
}
114+
115+
{
116+
117+
let resp!: Nested;
118+
const { resp: { data: dataAlias }, type } = resp;
119+
if (type === 'string') {
120+
dataAlias satisfies string;
121+
~~~~~~~~~
122+
!!! error TS1360: Type 'string | number' does not satisfy the expected type 'string'.
123+
!!! error TS1360: Type 'number' is not assignable to type 'string'.
124+
}
125+
if (resp.type === 'string') {
126+
resp.resp.data satisfies string;
127+
}
128+
}
129+
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
//// [tests/cases/compiler/controlFlowAliasedDiscriminants.ts] ////
2+
3+
//// [controlFlowAliasedDiscriminants.ts]
4+
type UseQueryResult<T> = {
5+
isSuccess: false;
6+
data: undefined;
7+
} | {
8+
isSuccess: true;
9+
data: T
10+
};
11+
12+
function useQuery(): UseQueryResult<number> {
13+
return {
14+
isSuccess: false,
15+
data: undefined,
16+
};
17+
}
18+
19+
const { data: data1, isSuccess: isSuccess1 } = useQuery();
20+
const { data: data2, isSuccess: isSuccess2 } = useQuery();
21+
const { data: data3, isSuccess: isSuccess3 } = useQuery();
22+
23+
if (isSuccess1 && isSuccess2 && isSuccess3) {
24+
data1.toExponential(); // should ok
25+
data2.toExponential(); // should ok
26+
data3.toExponential(); // should ok
27+
}
28+
29+
const areSuccess = isSuccess1 && isSuccess2 && isSuccess3;
30+
if (areSuccess) {
31+
data1.toExponential(); // should ok
32+
data2.toExponential(); // should ok
33+
data3.toExponential(); // should ok
34+
}
35+
36+
{
37+
let { data: data1, isSuccess: isSuccess1 } = useQuery();
38+
let { data: data2, isSuccess: isSuccess2 } = useQuery();
39+
const { data: data3, isSuccess: isSuccess3 } = useQuery();
40+
const areSuccess = isSuccess1 && isSuccess2 && isSuccess3;
41+
if (areSuccess) {
42+
data1.toExponential(); // should error
43+
data2.toExponential(); // should error
44+
data3.toExponential(); // should ok
45+
}
46+
}
47+
48+
declare function getArrayResult(): [true, number] | [false, undefined];
49+
{
50+
const [foo1, bar1] = getArrayResult();
51+
const [foo2, bar2] = getArrayResult();
52+
const [foo3, bar3] = getArrayResult();
53+
const arrayAllSuccess = foo1 && foo2 && foo3;
54+
if (arrayAllSuccess) {
55+
bar1.toExponential(); // should ok
56+
bar2.toExponential(); // should ok
57+
bar3.toExponential(); // should ok
58+
}
59+
}
60+
61+
{
62+
const [foo1, bar1] = getArrayResult();
63+
let [foo2, bar2] = getArrayResult();
64+
let [foo3, bar3] = getArrayResult();
65+
const arrayAllSuccess = foo1 && foo2 && foo3;
66+
if (arrayAllSuccess) {
67+
bar1.toExponential(); // should ok
68+
bar2.toExponential(); // should error
69+
bar3.toExponential(); // should error
70+
}
71+
}
72+
73+
type Nested = {
74+
type: 'string';
75+
resp: {
76+
data: string
77+
}
78+
} | {
79+
type: 'number';
80+
resp: {
81+
data: number;
82+
}
83+
}
84+
85+
{
86+
let resp!: Nested;
87+
const { resp: { data }, type } = resp;
88+
if (type === 'string') {
89+
data satisfies string;
90+
}
91+
if (resp.type === 'string') {
92+
resp.resp.data satisfies string;
93+
}
94+
}
95+
96+
{
97+
98+
let resp!: Nested;
99+
const { resp: { data: dataAlias }, type } = resp;
100+
if (type === 'string') {
101+
dataAlias satisfies string;
102+
}
103+
if (resp.type === 'string') {
104+
resp.resp.data satisfies string;
105+
}
106+
}
107+
108+
109+
//// [controlFlowAliasedDiscriminants.js]
110+
function useQuery() {
111+
return {
112+
isSuccess: false,
113+
data: undefined,
114+
};
115+
}
116+
var _a = useQuery(), data1 = _a.data, isSuccess1 = _a.isSuccess;
117+
var _b = useQuery(), data2 = _b.data, isSuccess2 = _b.isSuccess;
118+
var _c = useQuery(), data3 = _c.data, isSuccess3 = _c.isSuccess;
119+
if (isSuccess1 && isSuccess2 && isSuccess3) {
120+
data1.toExponential(); // should ok
121+
data2.toExponential(); // should ok
122+
data3.toExponential(); // should ok
123+
}
124+
var areSuccess = isSuccess1 && isSuccess2 && isSuccess3;
125+
if (areSuccess) {
126+
data1.toExponential(); // should ok
127+
data2.toExponential(); // should ok
128+
data3.toExponential(); // should ok
129+
}
130+
{
131+
var _d = useQuery(), data1_1 = _d.data, isSuccess1_1 = _d.isSuccess;
132+
var _e = useQuery(), data2_1 = _e.data, isSuccess2_1 = _e.isSuccess;
133+
var _f = useQuery(), data3_1 = _f.data, isSuccess3_1 = _f.isSuccess;
134+
var areSuccess_1 = isSuccess1_1 && isSuccess2_1 && isSuccess3_1;
135+
if (areSuccess_1) {
136+
data1_1.toExponential(); // should error
137+
data2_1.toExponential(); // should error
138+
data3_1.toExponential(); // should ok
139+
}
140+
}
141+
{
142+
var _g = getArrayResult(), foo1 = _g[0], bar1 = _g[1];
143+
var _h = getArrayResult(), foo2 = _h[0], bar2 = _h[1];
144+
var _j = getArrayResult(), foo3 = _j[0], bar3 = _j[1];
145+
var arrayAllSuccess = foo1 && foo2 && foo3;
146+
if (arrayAllSuccess) {
147+
bar1.toExponential(); // should ok
148+
bar2.toExponential(); // should ok
149+
bar3.toExponential(); // should ok
150+
}
151+
}
152+
{
153+
var _k = getArrayResult(), foo1 = _k[0], bar1 = _k[1];
154+
var _l = getArrayResult(), foo2 = _l[0], bar2 = _l[1];
155+
var _m = getArrayResult(), foo3 = _m[0], bar3 = _m[1];
156+
var arrayAllSuccess = foo1 && foo2 && foo3;
157+
if (arrayAllSuccess) {
158+
bar1.toExponential(); // should ok
159+
bar2.toExponential(); // should error
160+
bar3.toExponential(); // should error
161+
}
162+
}
163+
{
164+
var resp = void 0;
165+
var data = resp.resp.data, type = resp.type;
166+
if (type === 'string') {
167+
data;
168+
}
169+
if (resp.type === 'string') {
170+
resp.resp.data;
171+
}
172+
}
173+
{
174+
var resp = void 0;
175+
var dataAlias = resp.resp.data, type = resp.type;
176+
if (type === 'string') {
177+
dataAlias;
178+
}
179+
if (resp.type === 'string') {
180+
resp.resp.data;
181+
}
182+
}

0 commit comments

Comments
 (0)