Skip to content

Commit 44d9f91

Browse files
committed
Add a failing test case when the discriminant is a property of a parameter
1 parent 35f7909 commit 44d9f91

File tree

5 files changed

+372
-0
lines changed

5 files changed

+372
-0
lines changed
Lines changed: 245 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,245 @@
1+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(236,17): error TS2531: Object is possibly 'null'.
2+
3+
4+
==== tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts (1 errors) ====
5+
type Action =
6+
| { kind: 'A', payload: number }
7+
| { kind: 'B', payload: string };
8+
9+
function f10({ kind, payload }: Action) {
10+
if (kind === 'A') {
11+
payload.toFixed();
12+
}
13+
if (kind === 'B') {
14+
payload.toUpperCase();
15+
}
16+
}
17+
18+
function f11(action: Action) {
19+
const { kind, payload } = action;
20+
if (kind === 'A') {
21+
payload.toFixed();
22+
}
23+
if (kind === 'B') {
24+
payload.toUpperCase();
25+
}
26+
}
27+
28+
function f12({ kind, payload }: Action) {
29+
switch (kind) {
30+
case 'A':
31+
payload.toFixed();
32+
break;
33+
case 'B':
34+
payload.toUpperCase();
35+
break;
36+
default:
37+
payload; // never
38+
}
39+
}
40+
41+
type Action2 =
42+
| { kind: 'A', payload: number | undefined }
43+
| { kind: 'B', payload: string | undefined };
44+
45+
function f20({ kind, payload }: Action2) {
46+
if (payload) {
47+
if (kind === 'A') {
48+
payload.toFixed();
49+
}
50+
if (kind === 'B') {
51+
payload.toUpperCase();
52+
}
53+
}
54+
}
55+
56+
function f21(action: Action2) {
57+
const { kind, payload } = action;
58+
if (payload) {
59+
if (kind === 'A') {
60+
payload.toFixed();
61+
}
62+
if (kind === 'B') {
63+
payload.toUpperCase();
64+
}
65+
}
66+
}
67+
68+
function f22(action: Action2) {
69+
if (action.payload) {
70+
const { kind, payload } = action;
71+
if (kind === 'A') {
72+
payload.toFixed();
73+
}
74+
if (kind === 'B') {
75+
payload.toUpperCase();
76+
}
77+
}
78+
}
79+
80+
function f23({ kind, payload }: Action2) {
81+
if (payload) {
82+
switch (kind) {
83+
case 'A':
84+
payload.toFixed();
85+
break;
86+
case 'B':
87+
payload.toUpperCase();
88+
break;
89+
default:
90+
payload; // never
91+
}
92+
}
93+
}
94+
95+
type Foo =
96+
| { kind: 'A', isA: true }
97+
| { kind: 'B', isA: false }
98+
| { kind: 'C', isA: false };
99+
100+
function f30({ kind, isA }: Foo) {
101+
if (kind === 'A') {
102+
isA; // true
103+
}
104+
if (kind === 'B') {
105+
isA; // false
106+
}
107+
if (kind === 'C') {
108+
isA; // false
109+
}
110+
if (isA) {
111+
kind; // 'A'
112+
}
113+
else {
114+
kind; // 'B' | 'C'
115+
}
116+
}
117+
118+
type Args = ['A', number] | ['B', string]
119+
120+
function f40(...[kind, data]: Args) {
121+
if (kind === 'A') {
122+
data.toFixed();
123+
}
124+
if (kind === 'B') {
125+
data.toUpperCase();
126+
}
127+
}
128+
129+
// Repro from #35283
130+
131+
interface A<T> { variant: 'a', value: T }
132+
133+
interface B<T> { variant: 'b', value: Array<T> }
134+
135+
type AB<T> = A<T> | B<T>;
136+
137+
declare function printValue<T>(t: T): void;
138+
139+
declare function printValueList<T>(t: Array<T>): void;
140+
141+
function unrefined1<T>(ab: AB<T>): void {
142+
const { variant, value } = ab;
143+
if (variant === 'a') {
144+
printValue<T>(value);
145+
}
146+
else {
147+
printValueList<T>(value);
148+
}
149+
}
150+
151+
// Repro from #38020
152+
153+
type Action3 =
154+
| {type: 'add', payload: { toAdd: number } }
155+
| {type: 'remove', payload: { toRemove: number } };
156+
157+
const reducerBroken = (state: number, { type, payload }: Action3) => {
158+
switch (type) {
159+
case 'add':
160+
return state + payload.toAdd;
161+
case 'remove':
162+
return state - payload.toRemove;
163+
}
164+
}
165+
166+
// Repro from #46143
167+
168+
declare var it: Iterator<number>;
169+
const { value, done } = it.next();
170+
if (!done) {
171+
value; // number
172+
}
173+
174+
// Repro from #46658
175+
176+
declare function f50(cb: (...args: Args) => void): void
177+
178+
f50((kind, data) => {
179+
if (kind === 'A') {
180+
data.toFixed();
181+
}
182+
if (kind === 'B') {
183+
data.toUpperCase();
184+
}
185+
});
186+
187+
const f51: (...args: ['A', number] | ['B', string]) => void = (kind, payload) => {
188+
if (kind === 'A') {
189+
payload.toFixed();
190+
}
191+
if (kind === 'B') {
192+
payload.toUpperCase();
193+
}
194+
};
195+
196+
const f52: (...args: ['A', number] | ['B']) => void = (kind, payload?) => {
197+
if (kind === 'A') {
198+
payload.toFixed();
199+
}
200+
else {
201+
payload; // undefined
202+
}
203+
};
204+
205+
declare function readFile(path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void): void;
206+
207+
readFile('hello', (err, data) => {
208+
if (err === null) {
209+
data.length;
210+
}
211+
else {
212+
err.message;
213+
}
214+
});
215+
216+
type ReducerArgs = ["add", { a: number, b: number }] | ["concat", { firstArr: any[], secondArr: any[] }];
217+
218+
const reducer: (...args: ReducerArgs) => void = (op, args) => {
219+
switch (op) {
220+
case "add":
221+
console.log(args.a + args.b);
222+
break;
223+
case "concat":
224+
console.log(args.firstArr.concat(args.secondArr));
225+
break;
226+
}
227+
}
228+
229+
reducer("add", { a: 1, b: 3 });
230+
reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] });
231+
232+
type XStateActionArgs =
233+
| [event: { type: "LOG_OUT" }, context: { user: { name: string } }]
234+
| [event: { type: "LOG_IN" }, context: { user: null }];
235+
const xstateAction: (...args: XStateActionArgs) => void = (
236+
event,
237+
context
238+
) => {
239+
if (event.type === 'LOG_OUT') {
240+
console.log(context.user.name)
241+
~~~~~~~~~~~~
242+
!!! error TS2531: Object is possibly 'null'.
243+
}
244+
};
245+

tests/baselines/reference/dependentDestructuredVariables.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,18 @@ const reducer: (...args: ReducerArgs) => void = (op, args) => {
225225

226226
reducer("add", { a: 1, b: 3 });
227227
reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] });
228+
229+
type XStateActionArgs =
230+
| [event: { type: "LOG_OUT" }, context: { user: { name: string } }]
231+
| [event: { type: "LOG_IN" }, context: { user: null }];
232+
const xstateAction: (...args: XStateActionArgs) => void = (
233+
event,
234+
context
235+
) => {
236+
if (event.type === 'LOG_OUT') {
237+
console.log(context.user.name)
238+
}
239+
};
228240

229241

230242
//// [dependentDestructuredVariables.js]
@@ -394,6 +406,11 @@ const reducer = (op, args) => {
394406
};
395407
reducer("add", { a: 1, b: 3 });
396408
reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] });
409+
const xstateAction = (event, context) => {
410+
if (event.type === 'LOG_OUT') {
411+
console.log(context.user.name);
412+
}
413+
};
397414

398415

399416
//// [dependentDestructuredVariables.d.ts]
@@ -469,3 +486,15 @@ declare type ReducerArgs = ["add", {
469486
secondArr: any[];
470487
}];
471488
declare const reducer: (...args: ReducerArgs) => void;
489+
declare type XStateActionArgs = [event: {
490+
type: "LOG_OUT";
491+
}, context: {
492+
user: {
493+
name: string;
494+
};
495+
}] | [event: {
496+
type: "LOG_IN";
497+
}, context: {
498+
user: null;
499+
}];
500+
declare const xstateAction: (...args: XStateActionArgs) => void;

tests/baselines/reference/dependentDestructuredVariables.symbols

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -595,3 +595,44 @@ reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] });
595595
>firstArr : Symbol(firstArr, Decl(dependentDestructuredVariables.ts, 225, 19))
596596
>secondArr : Symbol(secondArr, Decl(dependentDestructuredVariables.ts, 225, 37))
597597

598+
type XStateActionArgs =
599+
>XStateActionArgs : Symbol(XStateActionArgs, Decl(dependentDestructuredVariables.ts, 225, 59))
600+
601+
| [event: { type: "LOG_OUT" }, context: { user: { name: string } }]
602+
>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 228, 13))
603+
>user : Symbol(user, Decl(dependentDestructuredVariables.ts, 228, 43))
604+
>name : Symbol(name, Decl(dependentDestructuredVariables.ts, 228, 51))
605+
606+
| [event: { type: "LOG_IN" }, context: { user: null }];
607+
>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 229, 13))
608+
>user : Symbol(user, Decl(dependentDestructuredVariables.ts, 229, 42))
609+
610+
const xstateAction: (...args: XStateActionArgs) => void = (
611+
>xstateAction : Symbol(xstateAction, Decl(dependentDestructuredVariables.ts, 230, 5))
612+
>args : Symbol(args, Decl(dependentDestructuredVariables.ts, 230, 21))
613+
>XStateActionArgs : Symbol(XStateActionArgs, Decl(dependentDestructuredVariables.ts, 225, 59))
614+
615+
event,
616+
>event : Symbol(event, Decl(dependentDestructuredVariables.ts, 230, 59))
617+
618+
context
619+
>context : Symbol(context, Decl(dependentDestructuredVariables.ts, 231, 8))
620+
621+
) => {
622+
if (event.type === 'LOG_OUT') {
623+
>event.type : Symbol(type, Decl(dependentDestructuredVariables.ts, 228, 13), Decl(dependentDestructuredVariables.ts, 229, 13))
624+
>event : Symbol(event, Decl(dependentDestructuredVariables.ts, 230, 59))
625+
>type : Symbol(type, Decl(dependentDestructuredVariables.ts, 228, 13), Decl(dependentDestructuredVariables.ts, 229, 13))
626+
627+
console.log(context.user.name)
628+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
629+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
630+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
631+
>context.user.name : Symbol(name, Decl(dependentDestructuredVariables.ts, 228, 51))
632+
>context.user : Symbol(user, Decl(dependentDestructuredVariables.ts, 228, 43), Decl(dependentDestructuredVariables.ts, 229, 42))
633+
>context : Symbol(context, Decl(dependentDestructuredVariables.ts, 231, 8))
634+
>user : Symbol(user, Decl(dependentDestructuredVariables.ts, 228, 43), Decl(dependentDestructuredVariables.ts, 229, 42))
635+
>name : Symbol(name, Decl(dependentDestructuredVariables.ts, 228, 51))
636+
}
637+
};
638+

tests/baselines/reference/dependentDestructuredVariables.types

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,3 +677,48 @@ reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] });
677677
>3 : 3
678678
>4 : 4
679679

680+
type XStateActionArgs =
681+
>XStateActionArgs : XStateActionArgs
682+
683+
| [event: { type: "LOG_OUT" }, context: { user: { name: string } }]
684+
>type : "LOG_OUT"
685+
>user : { name: string; }
686+
>name : string
687+
688+
| [event: { type: "LOG_IN" }, context: { user: null }];
689+
>type : "LOG_IN"
690+
>user : null
691+
>null : null
692+
693+
const xstateAction: (...args: XStateActionArgs) => void = (
694+
>xstateAction : (...args: XStateActionArgs) => void
695+
>args : XStateActionArgs
696+
>( event, context) => { if (event.type === 'LOG_OUT') { console.log(context.user.name) }} : (event: { type: "LOG_OUT"; } | { type: "LOG_IN"; }, context: { user: { name: string; }; } | { user: null; }) => void
697+
698+
event,
699+
>event : { type: "LOG_OUT"; } | { type: "LOG_IN"; }
700+
701+
context
702+
>context : { user: { name: string; }; } | { user: null; }
703+
704+
) => {
705+
if (event.type === 'LOG_OUT') {
706+
>event.type === 'LOG_OUT' : boolean
707+
>event.type : "LOG_OUT" | "LOG_IN"
708+
>event : { type: "LOG_OUT"; } | { type: "LOG_IN"; }
709+
>type : "LOG_OUT" | "LOG_IN"
710+
>'LOG_OUT' : "LOG_OUT"
711+
712+
console.log(context.user.name)
713+
>console.log(context.user.name) : void
714+
>console.log : (...data: any[]) => void
715+
>console : Console
716+
>log : (...data: any[]) => void
717+
>context.user.name : string
718+
>context.user : { name: string; } | null
719+
>context : { user: { name: string; }; } | { user: null; }
720+
>user : { name: string; } | null
721+
>name : string
722+
}
723+
};
724+

0 commit comments

Comments
 (0)