Skip to content

Commit 738b26f

Browse files
committed
Merge pull request microsoft#5145 from Microsoft/fixAsyncTypeChecks
Fixes some checker errors regarding async functions. Fixes microsoft#5115.
2 parents 4d3c74f + 703af07 commit 738b26f

4 files changed

Lines changed: 183 additions & 11 deletions

File tree

src/compiler/checker.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2209,7 +2209,7 @@ namespace ts {
22092209

22102210
/**
22112211
* Push an entry on the type resolution stack. If an entry with the given target and the given property name
2212-
* is already on the stack, and no entries in between already have a type, then a circularity has occurred.
2212+
* is already on the stack, and no entries in between already have a type, then a circularity has occurred.
22132213
* In this case, the result values of the existing entry and all entries pushed after it are changed to false,
22142214
* and the value false is returned. Otherwise, the new entry is just pushed onto the stack, and true is returned.
22152215
* In order to see if the same query has already been done before, the target object and the propertyName both
@@ -5243,7 +5243,7 @@ namespace ts {
52435243
// Only want to compare the construct signatures for abstractness guarantees.
52445244

52455245
// Because the "abstractness" of a class is the same across all construct signatures
5246-
// (internally we are checking the corresponding declaration), it is enough to perform
5246+
// (internally we are checking the corresponding declaration), it is enough to perform
52475247
// the check and report an error once over all pairs of source and target construct signatures.
52485248
//
52495249
// sourceSig and targetSig are (possibly) undefined.
@@ -6642,7 +6642,7 @@ namespace ts {
66426642
let needToCaptureLexicalThis = false;
66436643

66446644
if (!isCallExpression) {
6645-
// adjust the container reference in case if super is used inside arrow functions with arbitrary deep nesting
6645+
// adjust the container reference in case if super is used inside arrow functions with arbitrary deep nesting
66466646
while (container && container.kind === SyntaxKind.ArrowFunction) {
66476647
container = getSuperContainer(container, /*includeFunctions*/ true);
66486648
needToCaptureLexicalThis = languageVersion < ScriptTarget.ES6;
@@ -6652,7 +6652,7 @@ namespace ts {
66526652
let canUseSuperExpression = isLegalUsageOfSuperExpression(container);
66536653
let nodeCheckFlag: NodeCheckFlags = 0;
66546654

6655-
// always set NodeCheckFlags for 'super' expression node
6655+
// always set NodeCheckFlags for 'super' expression node
66566656
if (canUseSuperExpression) {
66576657
if ((container.flags & NodeFlags.Static) || isCallExpression) {
66586658
nodeCheckFlag = NodeCheckFlags.SuperStatic;
@@ -8568,7 +8568,7 @@ namespace ts {
85688568
// A method or accessor declaration decorator will have two or three arguments (see
85698569
// `PropertyDecorator` and `MethodDecorator` in core.d.ts)
85708570

8571-
// If we are emitting decorators for ES3, we will only pass two arguments.
8571+
// If we are emitting decorators for ES3, we will only pass two arguments.
85728572
if (languageVersion === ScriptTarget.ES3) {
85738573
return 2;
85748574
}
@@ -11306,7 +11306,8 @@ namespace ts {
1130611306
}
1130711307

1130811308
function checkNonThenableType(type: Type, location?: Node, message?: DiagnosticMessage) {
11309-
if (!(type.flags & TypeFlags.Any) && isTypeAssignableTo(type, getGlobalThenableType())) {
11309+
type = getWidenedType(type);
11310+
if (!isTypeAny(type) && isTypeAssignableTo(type, getGlobalThenableType())) {
1131011311
if (location) {
1131111312
if (!message) {
1131211313
message = Diagnostics.Operand_for_await_does_not_have_a_valid_callable_then_member;
@@ -12544,7 +12545,12 @@ namespace ts {
1254412545
if (isAsyncFunctionLike(func)) {
1254512546
let promisedType = getPromisedType(returnType);
1254612547
let awaitedType = checkAwaitedType(exprType, node.expression, Diagnostics.Return_expression_in_async_function_does_not_have_a_valid_callable_then_member);
12547-
checkTypeAssignableTo(awaitedType, promisedType, node.expression);
12548+
if (promisedType) {
12549+
// If the function has a return type, but promisedType is
12550+
// undefined, an error will be reported in checkAsyncFunctionReturnType
12551+
// so we don't need to report one here.
12552+
checkTypeAssignableTo(awaitedType, promisedType, node.expression);
12553+
}
1254812554
}
1254912555
else {
1255012556
checkTypeAssignableTo(exprType, returnType, node.expression);
@@ -13154,13 +13160,13 @@ namespace ts {
1315413160
autoValue = computeConstantValueForEnumMemberInitializer(initializer, enumType, enumIsConst, ambient);
1315513161
}
1315613162
else if (ambient && !enumIsConst) {
13157-
// In ambient enum declarations that specify no const modifier, enum member declarations
13163+
// In ambient enum declarations that specify no const modifier, enum member declarations
1315813164
// that omit a value are considered computed members (as opposed to having auto-incremented values assigned).
1315913165
autoValue = undefined;
1316013166
}
1316113167
else if (previousEnumMemberIsNonConstant) {
13162-
// If the member declaration specifies no value, the member is considered a constant enum member.
13163-
// If the member is the first member in the enum declaration, it is assigned the value zero.
13168+
// If the member declaration specifies no value, the member is considered a constant enum member.
13169+
// If the member is the first member in the enum declaration, it is assigned the value zero.
1316413170
// Otherwise, it is assigned the value of the immediately preceding member plus one,
1316513171
// and an error occurs if the immediately preceding member is not a constant enum member
1316613172
error(member.name, Diagnostics.Enum_member_must_have_initializer);
@@ -14100,7 +14106,7 @@ namespace ts {
1410014106
case SyntaxKind.ClassDeclaration:
1410114107
case SyntaxKind.InterfaceDeclaration:
1410214108
// If we didn't come from static member of class or interface,
14103-
// add the type parameters into the symbol table
14109+
// add the type parameters into the symbol table
1410414110
// (type parameters of classDeclaration/classExpression and interface are in member property of the symbol.
1410514111
// Note: that the memberFlags come from previous iteration.
1410614112
if (!(memberFlags & NodeFlags.Static)) {
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(6,16): error TS1055: Type '{}' is not a valid async function return type.
2+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(7,16): error TS1055: Type 'any' is not a valid async function return type.
3+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(8,16): error TS1055: Type 'number' is not a valid async function return type.
4+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(9,16): error TS1055: Type 'PromiseLike<void>' is not a valid async function return type.
5+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(10,16): error TS1055: Type 'typeof Thenable' is not a valid async function return type.
6+
Type 'Thenable' is not assignable to type 'PromiseLike<any>'.
7+
Types of property 'then' are incompatible.
8+
Type '() => void' is not assignable to type '{ <TResult>(onfulfilled?: (value: any) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<TResult>; <TResult>(onfulfilled?: (value: any) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): PromiseLike<TResult>; }'.
9+
Type 'void' is not assignable to type 'PromiseLike<any>'.
10+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(17,16): error TS1059: Return expression in async function does not have a valid callable 'then' member.
11+
tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts(23,25): error TS1058: Operand for 'await' does not have a valid callable 'then' member.
12+
13+
14+
==== tests/cases/conformance/async/es6/functionDeclarations/asyncFunctionDeclaration15_es6.ts (7 errors) ====
15+
declare class Thenable { then(): void; }
16+
declare let a: any;
17+
declare let obj: { then: string; };
18+
declare let thenable: Thenable;
19+
async function fn1() { } // valid: Promise<void>
20+
async function fn2(): { } { } // error
21+
~~~
22+
!!! error TS1055: Type '{}' is not a valid async function return type.
23+
async function fn3(): any { } // error
24+
~~~
25+
!!! error TS1055: Type 'any' is not a valid async function return type.
26+
async function fn4(): number { } // error
27+
~~~
28+
!!! error TS1055: Type 'number' is not a valid async function return type.
29+
async function fn5(): PromiseLike<void> { } // error
30+
~~~
31+
!!! error TS1055: Type 'PromiseLike<void>' is not a valid async function return type.
32+
async function fn6(): Thenable { } // error
33+
~~~
34+
!!! error TS1055: Type 'typeof Thenable' is not a valid async function return type.
35+
!!! error TS1055: Type 'Thenable' is not assignable to type 'PromiseLike<any>'.
36+
!!! error TS1055: Types of property 'then' are incompatible.
37+
!!! error TS1055: Type '() => void' is not assignable to type '{ <TResult>(onfulfilled?: (value: any) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => TResult | PromiseLike<TResult>): PromiseLike<TResult>; <TResult>(onfulfilled?: (value: any) => TResult | PromiseLike<TResult>, onrejected?: (reason: any) => void): PromiseLike<TResult>; }'.
38+
!!! error TS1055: Type 'void' is not assignable to type 'PromiseLike<any>'.
39+
async function fn7() { return; } // valid: Promise<void>
40+
async function fn8() { return 1; } // valid: Promise<number>
41+
async function fn9() { return null; } // valid: Promise<any>
42+
async function fn10() { return undefined; } // valid: Promise<any>
43+
async function fn11() { return a; } // valid: Promise<any>
44+
async function fn12() { return obj; } // valid: Promise<{ then: string; }>
45+
async function fn13() { return thenable; } // error
46+
~~~~
47+
!!! error TS1059: Return expression in async function does not have a valid callable 'then' member.
48+
async function fn14() { await 1; } // valid: Promise<void>
49+
async function fn15() { await null; } // valid: Promise<void>
50+
async function fn16() { await undefined; } // valid: Promise<void>
51+
async function fn17() { await a; } // valid: Promise<void>
52+
async function fn18() { await obj; } // valid: Promise<void>
53+
async function fn19() { await thenable; } // error
54+
~~~~~~~~~~~~~~
55+
!!! error TS1058: Operand for 'await' does not have a valid callable 'then' member.
56+
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//// [asyncFunctionDeclaration15_es6.ts]
2+
declare class Thenable { then(): void; }
3+
declare let a: any;
4+
declare let obj: { then: string; };
5+
declare let thenable: Thenable;
6+
async function fn1() { } // valid: Promise<void>
7+
async function fn2(): { } { } // error
8+
async function fn3(): any { } // error
9+
async function fn4(): number { } // error
10+
async function fn5(): PromiseLike<void> { } // error
11+
async function fn6(): Thenable { } // error
12+
async function fn7() { return; } // valid: Promise<void>
13+
async function fn8() { return 1; } // valid: Promise<number>
14+
async function fn9() { return null; } // valid: Promise<any>
15+
async function fn10() { return undefined; } // valid: Promise<any>
16+
async function fn11() { return a; } // valid: Promise<any>
17+
async function fn12() { return obj; } // valid: Promise<{ then: string; }>
18+
async function fn13() { return thenable; } // error
19+
async function fn14() { await 1; } // valid: Promise<void>
20+
async function fn15() { await null; } // valid: Promise<void>
21+
async function fn16() { await undefined; } // valid: Promise<void>
22+
async function fn17() { await a; } // valid: Promise<void>
23+
async function fn18() { await obj; } // valid: Promise<void>
24+
async function fn19() { await thenable; } // error
25+
26+
27+
//// [asyncFunctionDeclaration15_es6.js]
28+
function fn1() {
29+
return __awaiter(this, void 0, Promise, function* () { });
30+
} // valid: Promise<void>
31+
function fn2() {
32+
return __awaiter(this, void 0, Promise, function* () { });
33+
} // error
34+
function fn3() {
35+
return __awaiter(this, void 0, Promise, function* () { });
36+
} // error
37+
function fn4() {
38+
return __awaiter(this, void 0, Promise, function* () { });
39+
} // error
40+
function fn5() {
41+
return __awaiter(this, void 0, PromiseLike, function* () { });
42+
} // error
43+
function fn6() {
44+
return __awaiter(this, void 0, Thenable, function* () { });
45+
} // error
46+
function fn7() {
47+
return __awaiter(this, void 0, Promise, function* () { return; });
48+
} // valid: Promise<void>
49+
function fn8() {
50+
return __awaiter(this, void 0, Promise, function* () { return 1; });
51+
} // valid: Promise<number>
52+
function fn9() {
53+
return __awaiter(this, void 0, Promise, function* () { return null; });
54+
} // valid: Promise<any>
55+
function fn10() {
56+
return __awaiter(this, void 0, Promise, function* () { return undefined; });
57+
} // valid: Promise<any>
58+
function fn11() {
59+
return __awaiter(this, void 0, Promise, function* () { return a; });
60+
} // valid: Promise<any>
61+
function fn12() {
62+
return __awaiter(this, void 0, Promise, function* () { return obj; });
63+
} // valid: Promise<{ then: string; }>
64+
function fn13() {
65+
return __awaiter(this, void 0, Promise, function* () { return thenable; });
66+
} // error
67+
function fn14() {
68+
return __awaiter(this, void 0, Promise, function* () { yield 1; });
69+
} // valid: Promise<void>
70+
function fn15() {
71+
return __awaiter(this, void 0, Promise, function* () { yield null; });
72+
} // valid: Promise<void>
73+
function fn16() {
74+
return __awaiter(this, void 0, Promise, function* () { yield undefined; });
75+
} // valid: Promise<void>
76+
function fn17() {
77+
return __awaiter(this, void 0, Promise, function* () { yield a; });
78+
} // valid: Promise<void>
79+
function fn18() {
80+
return __awaiter(this, void 0, Promise, function* () { yield obj; });
81+
} // valid: Promise<void>
82+
function fn19() {
83+
return __awaiter(this, void 0, Promise, function* () { yield thenable; });
84+
} // error
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @target: ES6
2+
// @noEmitHelpers: true
3+
// @experimentalAsyncFunctions: true
4+
declare class Thenable { then(): void; }
5+
declare let a: any;
6+
declare let obj: { then: string; };
7+
declare let thenable: Thenable;
8+
async function fn1() { } // valid: Promise<void>
9+
async function fn2(): { } { } // error
10+
async function fn3(): any { } // error
11+
async function fn4(): number { } // error
12+
async function fn5(): PromiseLike<void> { } // error
13+
async function fn6(): Thenable { } // error
14+
async function fn7() { return; } // valid: Promise<void>
15+
async function fn8() { return 1; } // valid: Promise<number>
16+
async function fn9() { return null; } // valid: Promise<any>
17+
async function fn10() { return undefined; } // valid: Promise<any>
18+
async function fn11() { return a; } // valid: Promise<any>
19+
async function fn12() { return obj; } // valid: Promise<{ then: string; }>
20+
async function fn13() { return thenable; } // error
21+
async function fn14() { await 1; } // valid: Promise<void>
22+
async function fn15() { await null; } // valid: Promise<void>
23+
async function fn16() { await undefined; } // valid: Promise<void>
24+
async function fn17() { await a; } // valid: Promise<void>
25+
async function fn18() { await obj; } // valid: Promise<void>
26+
async function fn19() { await thenable; } // error

0 commit comments

Comments
 (0)