Skip to content

Commit 2c1fda2

Browse files
authored
Fixed false positive circular errors for await expressions with simple non-generic calls in CFA loops (#51126)
1 parent 0056761 commit 2c1fda2

10 files changed

+1036
-17
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ import {
427427
isAssignmentTarget,
428428
isAsyncFunction,
429429
isAutoAccessorPropertyDeclaration,
430+
isAwaitExpression,
430431
isBinaryExpression,
431432
isBindableObjectDefinePropertyCall,
432433
isBindableStaticElementAccessExpression,
@@ -36482,7 +36483,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3648236483
return type;
3648336484
}
3648436485

36485-
function getQuickTypeOfExpression(node: Expression) {
36486+
function getQuickTypeOfExpression(node: Expression): Type | undefined {
3648636487
let expr = skipParentheses(node, /*excludeJSDocTypeAssertions*/ true);
3648736488
if (isJSDocTypeAssertion(expr)) {
3648836489
const type = getJSDocTypeAssertionType(expr);
@@ -36491,14 +36492,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3649136492
}
3649236493
}
3649336494
expr = skipParentheses(node);
36495+
if (isAwaitExpression(expr)) {
36496+
const type = getQuickTypeOfExpression(expr.expression);
36497+
return type ? getAwaitedType(type) : undefined;
36498+
}
3649436499
// Optimize for the common case of a call to a function with a single non-generic call
3649536500
// signature where we can just fetch the return type without checking the arguments.
3649636501
if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*checkArgumentIsStringLiteralLike*/ true) && !isSymbolOrSymbolForCall(expr)) {
36497-
const type = isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) :
36502+
return isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) :
3649836503
getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression));
36499-
if (type) {
36500-
return type;
36501-
}
3650236504
}
3650336505
else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) {
3650436506
return getTypeFromTypeNode((expr as TypeAssertion).type);
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(11,23): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
2+
Type 'number' is not assignable to type 'string'.
3+
tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(22,23): error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
4+
Type 'number' is not assignable to type 'string'.
5+
tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(34,23): error TS2769: No overload matches this call.
6+
Overload 1 of 2, '(x: string): Promise<number>', gave the following error.
7+
Argument of type 'string | number' is not assignable to parameter of type 'string'.
8+
Type 'number' is not assignable to type 'string'.
9+
Overload 2 of 2, '(x: number): Promise<string>', gave the following error.
10+
Argument of type 'string | number' is not assignable to parameter of type 'number'.
11+
Type 'string' is not assignable to type 'number'.
12+
tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts(45,23): error TS2769: No overload matches this call.
13+
Overload 1 of 2, '(x: string): Promise<number>', gave the following error.
14+
Argument of type 'string | number' is not assignable to parameter of type 'string'.
15+
Type 'number' is not assignable to type 'string'.
16+
Overload 2 of 2, '(x: number): Promise<string>', gave the following error.
17+
Argument of type 'string | number' is not assignable to parameter of type 'number'.
18+
Type 'string' is not assignable to type 'number'.
19+
20+
21+
==== tests/cases/conformance/controlFlow/controlFlowIterationErrorsAsync.ts (4 errors) ====
22+
let cond: boolean;
23+
24+
async function len(s: string) {
25+
return s.length;
26+
}
27+
28+
async function f1() {
29+
let x: string | number | boolean;
30+
x = "";
31+
while (cond) {
32+
x = await len(x);
33+
~
34+
!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
35+
!!! error TS2345: Type 'number' is not assignable to type 'string'.
36+
x;
37+
}
38+
x;
39+
}
40+
41+
async function f2() {
42+
let x: string | number | boolean;
43+
x = "";
44+
while (cond) {
45+
x;
46+
x = await len(x);
47+
~
48+
!!! error TS2345: Argument of type 'string | number' is not assignable to parameter of type 'string'.
49+
!!! error TS2345: Type 'number' is not assignable to type 'string'.
50+
}
51+
x;
52+
}
53+
54+
declare function foo(x: string): Promise<number>;
55+
declare function foo(x: number): Promise<string>;
56+
57+
async function g1() {
58+
let x: string | number | boolean;
59+
x = "";
60+
while (cond) {
61+
x = await foo(x);
62+
~
63+
!!! error TS2769: No overload matches this call.
64+
!!! error TS2769: Overload 1 of 2, '(x: string): Promise<number>', gave the following error.
65+
!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'string'.
66+
!!! error TS2769: Type 'number' is not assignable to type 'string'.
67+
!!! error TS2769: Overload 2 of 2, '(x: number): Promise<string>', gave the following error.
68+
!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'number'.
69+
!!! error TS2769: Type 'string' is not assignable to type 'number'.
70+
x;
71+
}
72+
x;
73+
}
74+
75+
async function g2() {
76+
let x: string | number | boolean;
77+
x = "";
78+
while (cond) {
79+
x;
80+
x = await foo(x);
81+
~
82+
!!! error TS2769: No overload matches this call.
83+
!!! error TS2769: Overload 1 of 2, '(x: string): Promise<number>', gave the following error.
84+
!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'string'.
85+
!!! error TS2769: Type 'number' is not assignable to type 'string'.
86+
!!! error TS2769: Overload 2 of 2, '(x: number): Promise<string>', gave the following error.
87+
!!! error TS2769: Argument of type 'string | number' is not assignable to parameter of type 'number'.
88+
!!! error TS2769: Type 'string' is not assignable to type 'number'.
89+
}
90+
x;
91+
}
92+
93+
async function asNumber(x: string | number): Promise<number> {
94+
return +x;
95+
}
96+
97+
async function h1() {
98+
let x: string | number | boolean;
99+
x = "0";
100+
while (cond) {
101+
x = +x + 1;
102+
x;
103+
}
104+
}
105+
106+
async function h2() {
107+
let x: string | number | boolean;
108+
x = "0";
109+
while (cond) {
110+
x = await asNumber(x) + 1;
111+
x;
112+
}
113+
}
114+
115+
async function h3() {
116+
let x: string | number | boolean;
117+
x = "0";
118+
while (cond) {
119+
let y = await asNumber(x);
120+
x = y + 1;
121+
x;
122+
}
123+
}
124+
125+
async function h4() {
126+
let x: string | number | boolean;
127+
x = "0";
128+
while (cond) {
129+
x;
130+
let y = await asNumber(x);
131+
x = y + 1;
132+
x;
133+
}
134+
}
135+
136+
// repro #51115
137+
138+
async function get_things(_: number | undefined) {
139+
return [0];
140+
}
141+
142+
async function foobar() {
143+
let before: number | undefined = undefined;
144+
for (let i = 0; i < 2; i++) {
145+
const results = await get_things(before);
146+
before = results[0];
147+
}
148+
}
149+
150+
// repro #43047#issuecomment-821453073
151+
152+
declare function foox(x: string | undefined): Promise<string>
153+
154+
async () => {
155+
let bar: string | undefined = undefined;
156+
do {
157+
const baz = await foox(bar);
158+
bar = baz
159+
} while (bar)
160+
}
161+
162+
// repro #43047#issuecomment-874221939
163+
164+
declare function myQuery(input: { lastId: number | undefined }): Promise<{ entities: number[] }>;
165+
166+
async function myFunc(): Promise<void> {
167+
let lastId: number | undefined = undefined;
168+
169+
while (true) {
170+
const { entities } = await myQuery({
171+
lastId,
172+
});
173+
174+
lastId = entities[entities.length - 1];
175+
}
176+
}
177+

0 commit comments

Comments
 (0)