Skip to content

Commit 5db4e7a

Browse files
authored
Fix async function block return expr error in js (microsoft#37845)
1 parent fd6f922 commit 5db4e7a

File tree

5 files changed

+141
-13
lines changed

5 files changed

+141
-13
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -27244,7 +27244,7 @@ namespace ts {
2724427244
}
2724527245

2724627246
const functionFlags = getFunctionFlags(func);
27247-
const type = returnType && getReturnOrPromisedType(returnType, functionFlags);
27247+
const type = returnType && unwrapReturnType(returnType, functionFlags);
2724827248

2724927249
// Functions with with an explicitly specified 'void' or 'any' return type don't need any return expressions.
2725027250
if (type && maybeTypeOfKind(type, TypeFlags.Any | TypeFlags.Void)) {
@@ -27364,14 +27364,6 @@ namespace ts {
2736427364
}
2736527365
}
2736627366

27367-
function getReturnOrPromisedType(type: Type | undefined, functionFlags: FunctionFlags) {
27368-
const isGenerator = !!(functionFlags & FunctionFlags.Generator);
27369-
const isAsync = !!(functionFlags & FunctionFlags.Async);
27370-
return type && isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, type, isAsync) || errorType :
27371-
type && isAsync ? getAwaitedType(type) || errorType :
27372-
type;
27373-
}
27374-
2737527367
function checkFunctionExpressionOrObjectLiteralMethodDeferred(node: ArrowFunction | FunctionExpression | MethodDeclaration) {
2737627368
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
2737727369

@@ -27399,7 +27391,7 @@ namespace ts {
2739927391
// check assignability of the awaited type of the expression body against the promised type of
2740027392
// its return type annotation.
2740127393
const exprType = checkExpression(node.body);
27402-
const returnOrPromisedType = getReturnOrPromisedType(returnType, functionFlags);
27394+
const returnOrPromisedType = returnType && unwrapReturnType(returnType, functionFlags);
2740327395
if (returnOrPromisedType) {
2740427396
if ((functionFlags & FunctionFlags.AsyncGenerator) === FunctionFlags.Async) { // Async function
2740527397
const awaitedType = checkAwaitedType(exprType, node.body, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member);
@@ -32687,8 +32679,8 @@ namespace ts {
3268732679
function unwrapReturnType(returnType: Type, functionFlags: FunctionFlags) {
3268832680
const isGenerator = !!(functionFlags & FunctionFlags.Generator);
3268932681
const isAsync = !!(functionFlags & FunctionFlags.Async);
32690-
return isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, isAsync) || errorType :
32691-
isAsync ? getPromisedTypeOfPromise(returnType) || errorType :
32682+
return isGenerator ? getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, isAsync) ?? errorType :
32683+
isAsync ? getAwaitedType(returnType) ?? errorType :
3269232684
returnType;
3269332685
}
3269432686

@@ -32725,7 +32717,7 @@ namespace ts {
3272532717
}
3272632718
}
3272732719
else if (getReturnTypeFromAnnotation(func)) {
32728-
const unwrappedReturnType = unwrapReturnType(returnType, functionFlags);
32720+
const unwrappedReturnType = unwrapReturnType(returnType, functionFlags) ?? returnType;
3272932721
const unwrappedExprType = functionFlags & FunctionFlags.Async
3273032722
? checkAwaitedType(exprType, node, Diagnostics.The_return_type_of_an_async_function_must_either_be_a_valid_promise_or_must_not_contain_a_callable_then_member)
3273132723
: exprType;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
tests/cases/conformance/async/es2017/asyncArrowFunction/file.js(3,17): error TS2322: Type '0' is not assignable to type 'string'.
2+
tests/cases/conformance/async/es2017/asyncArrowFunction/file.js(7,23): error TS2322: Type '0' is not assignable to type 'string'.
3+
tests/cases/conformance/async/es2017/asyncArrowFunction/file.js(12,2): error TS2322: Type '0' is not assignable to type 'string'.
4+
tests/cases/conformance/async/es2017/asyncArrowFunction/file.js(19,3): error TS2345: Argument of type '() => Promise<number>' is not assignable to parameter of type '() => string'.
5+
Type 'Promise<number>' is not assignable to type 'string'.
6+
7+
8+
==== tests/cases/conformance/async/es2017/asyncArrowFunction/file.js (4 errors) ====
9+
// Error (good)
10+
/** @type {function(): string} */
11+
const a = () => 0
12+
~
13+
!!! error TS2322: Type '0' is not assignable to type 'string'.
14+
15+
// Error (good)
16+
/** @type {function(): string} */
17+
const b = async () => 0
18+
~
19+
!!! error TS2322: Type '0' is not assignable to type 'string'.
20+
21+
// No error (bad)
22+
/** @type {function(): string} */
23+
const c = async () => {
24+
return 0
25+
~~~~~~~~
26+
!!! error TS2322: Type '0' is not assignable to type 'string'.
27+
}
28+
29+
/** @type {function(function(): string): void} */
30+
const f = (p) => {}
31+
32+
// Error (good)
33+
f(async () => {
34+
~~~~~~~~~~~~~
35+
!!! error TS2345: Argument of type '() => Promise<number>' is not assignable to parameter of type '() => string'.
36+
!!! error TS2345: Type 'Promise<number>' is not assignable to type 'string'.
37+
return 0
38+
})
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
=== tests/cases/conformance/async/es2017/asyncArrowFunction/file.js ===
2+
// Error (good)
3+
/** @type {function(): string} */
4+
const a = () => 0
5+
>a : Symbol(a, Decl(file.js, 2, 5))
6+
7+
// Error (good)
8+
/** @type {function(): string} */
9+
const b = async () => 0
10+
>b : Symbol(b, Decl(file.js, 6, 5))
11+
12+
// No error (bad)
13+
/** @type {function(): string} */
14+
const c = async () => {
15+
>c : Symbol(c, Decl(file.js, 10, 5))
16+
17+
return 0
18+
}
19+
20+
/** @type {function(function(): string): void} */
21+
const f = (p) => {}
22+
>f : Symbol(f, Decl(file.js, 15, 5))
23+
>p : Symbol(p, Decl(file.js, 15, 11))
24+
25+
// Error (good)
26+
f(async () => {
27+
>f : Symbol(f, Decl(file.js, 15, 5))
28+
29+
return 0
30+
})
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
=== tests/cases/conformance/async/es2017/asyncArrowFunction/file.js ===
2+
// Error (good)
3+
/** @type {function(): string} */
4+
const a = () => 0
5+
>a : () => string
6+
>() => 0 : () => string
7+
>0 : 0
8+
9+
// Error (good)
10+
/** @type {function(): string} */
11+
const b = async () => 0
12+
>b : () => string
13+
>async () => 0 : () => string
14+
>0 : 0
15+
16+
// No error (bad)
17+
/** @type {function(): string} */
18+
const c = async () => {
19+
>c : () => string
20+
>async () => { return 0} : () => string
21+
22+
return 0
23+
>0 : 0
24+
}
25+
26+
/** @type {function(function(): string): void} */
27+
const f = (p) => {}
28+
>f : (arg0: () => string) => void
29+
>(p) => {} : (p: () => string) => void
30+
>p : () => string
31+
32+
// Error (good)
33+
f(async () => {
34+
>f(async () => { return 0}) : void
35+
>f : (arg0: () => string) => void
36+
>async () => { return 0} : () => Promise<number>
37+
38+
return 0
39+
>0 : 0
40+
41+
})
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @allowJs: true
2+
// @checkJs: true
3+
// @noEmit: true
4+
// @target: es2017
5+
// @filename: file.js
6+
7+
// Error (good)
8+
/** @type {function(): string} */
9+
const a = () => 0
10+
11+
// Error (good)
12+
/** @type {function(): string} */
13+
const b = async () => 0
14+
15+
// No error (bad)
16+
/** @type {function(): string} */
17+
const c = async () => {
18+
return 0
19+
}
20+
21+
/** @type {function(function(): string): void} */
22+
const f = (p) => {}
23+
24+
// Error (good)
25+
f(async () => {
26+
return 0
27+
})

0 commit comments

Comments
 (0)