Skip to content

Commit ef0095c

Browse files
Fix blocking of recursive dependencies in getNarrowedTypeOfSymbol (#48941) (#48943)
* Better blocking of recursive dependencies in getNarrowedTypeOfSymbol * Add regression test Co-authored-by: Anders Hejlsberg <[email protected]>
1 parent 81f1e5f commit ef0095c

6 files changed

+1153
-72
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25119,7 +25119,7 @@ namespace ts {
2511925119
if (isBindingElement(declaration) && !declaration.initializer && !declaration.dotDotDotToken && declaration.parent.elements.length >= 2) {
2512025120
const parent = declaration.parent.parent;
2512125121
if (parent.kind === SyntaxKind.VariableDeclaration && getCombinedNodeFlags(declaration) & NodeFlags.Const || parent.kind === SyntaxKind.Parameter) {
25122-
const links = getNodeLinks(location);
25122+
const links = getNodeLinks(parent);
2512325123
if (!(links.flags & NodeCheckFlags.InCheckIdentifier)) {
2512425124
links.flags |= NodeCheckFlags.InCheckIdentifier;
2512525125
const parentType = getTypeForBindingElementParent(parent, CheckMode.Normal);
Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
error TS2318: Cannot find global type 'AsyncIterableIterator'.
2+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(240,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
3+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(242,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
4+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(257,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
5+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(259,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
6+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(274,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
7+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(276,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
8+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(285,6): error TS2583: Cannot find name 'AsyncGenerator'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2018' or later.
9+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(285,6): error TS4057: Return type of method from exported interface has or is using private name 'AsyncGenerator'.
10+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(291,10): error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
11+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(293,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
12+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(304,17): error TS2339: Property 'toFixed' does not exist on type 'string | number'.
13+
Property 'toFixed' does not exist on type 'string'.
14+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(307,17): error TS2339: Property 'toUpperCase' does not exist on type 'string | number'.
15+
Property 'toUpperCase' does not exist on type 'number'.
16+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(314,5): error TS7022: 'value1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
17+
tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts(314,5): error TS7031: Binding element 'value1' implicitly has an 'any' type.
18+
19+
20+
!!! error TS2318: Cannot find global type 'AsyncIterableIterator'.
21+
==== tests/cases/conformance/controlFlow/dependentDestructuredVariables.ts (14 errors) ====
22+
type Action =
23+
| { kind: 'A', payload: number }
24+
| { kind: 'B', payload: string };
25+
26+
function f10({ kind, payload }: Action) {
27+
if (kind === 'A') {
28+
payload.toFixed();
29+
}
30+
if (kind === 'B') {
31+
payload.toUpperCase();
32+
}
33+
}
34+
35+
function f11(action: Action) {
36+
const { kind, payload } = action;
37+
if (kind === 'A') {
38+
payload.toFixed();
39+
}
40+
if (kind === 'B') {
41+
payload.toUpperCase();
42+
}
43+
}
44+
45+
function f12({ kind, payload }: Action) {
46+
switch (kind) {
47+
case 'A':
48+
payload.toFixed();
49+
break;
50+
case 'B':
51+
payload.toUpperCase();
52+
break;
53+
default:
54+
payload; // never
55+
}
56+
}
57+
58+
type Action2 =
59+
| { kind: 'A', payload: number | undefined }
60+
| { kind: 'B', payload: string | undefined };
61+
62+
function f20({ kind, payload }: Action2) {
63+
if (payload) {
64+
if (kind === 'A') {
65+
payload.toFixed();
66+
}
67+
if (kind === 'B') {
68+
payload.toUpperCase();
69+
}
70+
}
71+
}
72+
73+
function f21(action: Action2) {
74+
const { kind, payload } = action;
75+
if (payload) {
76+
if (kind === 'A') {
77+
payload.toFixed();
78+
}
79+
if (kind === 'B') {
80+
payload.toUpperCase();
81+
}
82+
}
83+
}
84+
85+
function f22(action: Action2) {
86+
if (action.payload) {
87+
const { kind, payload } = action;
88+
if (kind === 'A') {
89+
payload.toFixed();
90+
}
91+
if (kind === 'B') {
92+
payload.toUpperCase();
93+
}
94+
}
95+
}
96+
97+
function f23({ kind, payload }: Action2) {
98+
if (payload) {
99+
switch (kind) {
100+
case 'A':
101+
payload.toFixed();
102+
break;
103+
case 'B':
104+
payload.toUpperCase();
105+
break;
106+
default:
107+
payload; // never
108+
}
109+
}
110+
}
111+
112+
type Foo =
113+
| { kind: 'A', isA: true }
114+
| { kind: 'B', isA: false }
115+
| { kind: 'C', isA: false };
116+
117+
function f30({ kind, isA }: Foo) {
118+
if (kind === 'A') {
119+
isA; // true
120+
}
121+
if (kind === 'B') {
122+
isA; // false
123+
}
124+
if (kind === 'C') {
125+
isA; // false
126+
}
127+
if (isA) {
128+
kind; // 'A'
129+
}
130+
else {
131+
kind; // 'B' | 'C'
132+
}
133+
}
134+
135+
type Args = ['A', number] | ['B', string]
136+
137+
function f40(...[kind, data]: Args) {
138+
if (kind === 'A') {
139+
data.toFixed();
140+
}
141+
if (kind === 'B') {
142+
data.toUpperCase();
143+
}
144+
}
145+
146+
// Repro from #35283
147+
148+
interface A<T> { variant: 'a', value: T }
149+
150+
interface B<T> { variant: 'b', value: Array<T> }
151+
152+
type AB<T> = A<T> | B<T>;
153+
154+
declare function printValue<T>(t: T): void;
155+
156+
declare function printValueList<T>(t: Array<T>): void;
157+
158+
function unrefined1<T>(ab: AB<T>): void {
159+
const { variant, value } = ab;
160+
if (variant === 'a') {
161+
printValue<T>(value);
162+
}
163+
else {
164+
printValueList<T>(value);
165+
}
166+
}
167+
168+
// Repro from #38020
169+
170+
type Action3 =
171+
| {type: 'add', payload: { toAdd: number } }
172+
| {type: 'remove', payload: { toRemove: number } };
173+
174+
const reducerBroken = (state: number, { type, payload }: Action3) => {
175+
switch (type) {
176+
case 'add':
177+
return state + payload.toAdd;
178+
case 'remove':
179+
return state - payload.toRemove;
180+
}
181+
}
182+
183+
// Repro from #46143
184+
185+
declare var it: Iterator<number>;
186+
const { value, done } = it.next();
187+
if (!done) {
188+
value; // number
189+
}
190+
191+
// Repro from #46658
192+
193+
declare function f50(cb: (...args: Args) => void): void
194+
195+
f50((kind, data) => {
196+
if (kind === 'A') {
197+
data.toFixed();
198+
}
199+
if (kind === 'B') {
200+
data.toUpperCase();
201+
}
202+
});
203+
204+
const f51: (...args: ['A', number] | ['B', string]) => void = (kind, payload) => {
205+
if (kind === 'A') {
206+
payload.toFixed();
207+
}
208+
if (kind === 'B') {
209+
payload.toUpperCase();
210+
}
211+
};
212+
213+
const f52: (...args: ['A', number] | ['B']) => void = (kind, payload?) => {
214+
if (kind === 'A') {
215+
payload.toFixed();
216+
}
217+
else {
218+
payload; // undefined
219+
}
220+
};
221+
222+
declare function readFile(path: string, callback: (...args: [err: null, data: unknown[]] | [err: Error, data: undefined]) => void): void;
223+
224+
readFile('hello', (err, data) => {
225+
if (err === null) {
226+
data.length;
227+
}
228+
else {
229+
err.message;
230+
}
231+
});
232+
233+
type ReducerArgs = ["add", { a: number, b: number }] | ["concat", { firstArr: any[], secondArr: any[] }];
234+
235+
const reducer: (...args: ReducerArgs) => void = (op, args) => {
236+
switch (op) {
237+
case "add":
238+
console.log(args.a + args.b);
239+
break;
240+
case "concat":
241+
console.log(args.firstArr.concat(args.secondArr));
242+
break;
243+
}
244+
}
245+
246+
reducer("add", { a: 1, b: 3 });
247+
reducer("concat", { firstArr: [1, 2], secondArr: [3, 4] });
248+
249+
// repro from https://github.com/microsoft/TypeScript/pull/47190#issuecomment-1057603588
250+
251+
type FooMethod = {
252+
method(...args:
253+
[type: "str", cb: (e: string) => void] |
254+
[type: "num", cb: (e: number) => void]
255+
): void;
256+
}
257+
258+
let fooM: FooMethod = {
259+
method(type, cb) {
260+
if (type == 'num') {
261+
cb(123)
262+
~~~
263+
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
264+
} else {
265+
cb("abc")
266+
~~~~~
267+
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
268+
}
269+
}
270+
};
271+
272+
type FooAsyncMethod = {
273+
method(...args:
274+
[type: "str", cb: (e: string) => void] |
275+
[type: "num", cb: (e: number) => void]
276+
): Promise<any>;
277+
}
278+
279+
let fooAsyncM: FooAsyncMethod = {
280+
async method(type, cb) {
281+
if (type == 'num') {
282+
cb(123)
283+
~~~
284+
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
285+
} else {
286+
cb("abc")
287+
~~~~~
288+
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
289+
}
290+
}
291+
};
292+
293+
type FooGenMethod = {
294+
method(...args:
295+
[type: "str", cb: (e: string) => void] |
296+
[type: "num", cb: (e: number) => void]
297+
): Generator<any, any, any>;
298+
}
299+
300+
let fooGenM: FooGenMethod = {
301+
*method(type, cb) {
302+
if (type == 'num') {
303+
cb(123)
304+
~~~
305+
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
306+
} else {
307+
cb("abc")
308+
~~~~~
309+
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
310+
}
311+
}
312+
};
313+
314+
type FooAsyncGenMethod = {
315+
method(...args:
316+
[type: "str", cb: (e: string) => void] |
317+
[type: "num", cb: (e: number) => void]
318+
): AsyncGenerator<any, any, any>;
319+
~~~~~~~~~~~~~~
320+
!!! error TS2583: Cannot find name 'AsyncGenerator'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2018' or later.
321+
~~~~~~~~~~~~~~
322+
!!! error TS4057: Return type of method from exported interface has or is using private name 'AsyncGenerator'.
323+
}
324+
325+
let fooAsyncGenM: FooAsyncGenMethod = {
326+
async *method(type, cb) {
327+
if (type == 'num') {
328+
cb(123)
329+
~~~
330+
!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'never'.
331+
} else {
332+
cb("abc")
333+
~~~~~
334+
!!! error TS2345: Argument of type 'string' is not assignable to parameter of type 'never'.
335+
}
336+
}
337+
};
338+
339+
// Repro from #48345
340+
341+
type Func = <T extends ["a", number] | ["b", string]>(...args: T) => void;
342+
343+
const f60: Func = (kind, payload) => {
344+
if (kind === "a") {
345+
payload.toFixed(); // error
346+
~~~~~~~
347+
!!! error TS2339: Property 'toFixed' does not exist on type 'string | number'.
348+
!!! error TS2339: Property 'toFixed' does not exist on type 'string'.
349+
}
350+
if (kind === "b") {
351+
payload.toUpperCase(); // error
352+
~~~~~~~~~~~
353+
!!! error TS2339: Property 'toUpperCase' does not exist on type 'string | number'.
354+
!!! error TS2339: Property 'toUpperCase' does not exist on type 'number'.
355+
}
356+
};
357+
358+
// Repro from #48902
359+
360+
function foo({
361+
value1,
362+
~~~~~~
363+
!!! error TS7022: 'value1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.
364+
~~~~~~
365+
!!! error TS7031: Binding element 'value1' implicitly has an 'any' type.
366+
test1 = value1.test1,
367+
test2 = value1.test2,
368+
test3 = value1.test3,
369+
test4 = value1.test4,
370+
test5 = value1.test5,
371+
test6 = value1.test6,
372+
test7 = value1.test7,
373+
test8 = value1.test8,
374+
test9 = value1.test9
375+
}) {}
376+

0 commit comments

Comments
 (0)