Skip to content

Commit c6ee25d

Browse files
committed
Type checking for async iterables and async generators.
1 parent ed4fead commit c6ee25d

10 files changed

Lines changed: 580 additions & 312 deletions

File tree

Jakefile.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,8 @@ var es2016LibrarySourceMap = es2016LibrarySource.map(function (source) {
299299

300300
var es2017LibrarySource = [
301301
"es2017.object.d.ts",
302-
"es2017.sharedmemory.d.ts"
302+
"es2017.sharedmemory.d.ts",
303+
"es2017.asynciterable.d.ts"
303304
];
304305

305306
var es2017LibrarySourceMap = es2017LibrarySource.map(function (source) {

src/compiler/binder.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -895,8 +895,8 @@ namespace ts {
895895
const enclosingLabeledStatement = node.parent.kind === SyntaxKind.LabeledStatement
896896
? lastOrUndefined(activeLabels)
897897
: undefined;
898-
// if do statement is wrapped in labeled statement then target labels for break/continue with or without
899-
// label should be the same
898+
// if do statement is wrapped in labeled statement then target labels for break/continue with or without
899+
// label should be the same
900900
const preConditionLabel = enclosingLabeledStatement ? enclosingLabeledStatement.continueTarget : createBranchLabel();
901901
const postDoLabel = enclosingLabeledStatement ? enclosingLabeledStatement.breakTarget : createBranchLabel();
902902
addAntecedent(preDoLabel, currentFlow);
@@ -2291,7 +2291,7 @@ namespace ts {
22912291

22922292
function bindFunctionDeclaration(node: FunctionDeclaration) {
22932293
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
2294-
if (isAsyncFunctionLike(node)) {
2294+
if (isAsyncFunction(node)) {
22952295
emitFlags |= NodeFlags.HasAsyncFunctions;
22962296
}
22972297
}
@@ -2308,7 +2308,7 @@ namespace ts {
23082308

23092309
function bindFunctionExpression(node: FunctionExpression) {
23102310
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
2311-
if (isAsyncFunctionLike(node)) {
2311+
if (isAsyncFunction(node)) {
23122312
emitFlags |= NodeFlags.HasAsyncFunctions;
23132313
}
23142314
}
@@ -2322,7 +2322,7 @@ namespace ts {
23222322

23232323
function bindPropertyOrMethodOrAccessor(node: Declaration, symbolFlags: SymbolFlags, symbolExcludes: SymbolFlags) {
23242324
if (!isDeclarationFile(file) && !isInAmbientContext(node)) {
2325-
if (isAsyncFunctionLike(node)) {
2325+
if (isAsyncFunction(node)) {
23262326
emitFlags |= NodeFlags.HasAsyncFunctions;
23272327
}
23282328
if (nodeIsDecorated(node)) {

src/compiler/checker.ts

Lines changed: 473 additions & 292 deletions
Large diffs are not rendered by default.

src/compiler/diagnosticMessages.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,23 +175,23 @@
175175
"category": "Error",
176176
"code": 1057
177177
},
178-
"Operand for 'await' does not have a valid callable 'then' member.": {
178+
"Type used as operand to 'await' or the return type of an async function must not contain a callable 'then' member if it is not a promise.": {
179179
"category": "Error",
180180
"code": 1058
181181
},
182-
"Return expression in async function does not have a valid callable 'then' member.": {
182+
"A promise must have a 'then' method.": {
183183
"category": "Error",
184184
"code": 1059
185185
},
186-
"Expression body for async arrow function does not have a valid callable 'then' member.": {
186+
"The first parameter of the 'then' method of a promise must be a callback.": {
187187
"category": "Error",
188188
"code": 1060
189189
},
190190
"Enum member must have initializer.": {
191191
"category": "Error",
192192
"code": 1061
193193
},
194-
"{0} is referenced directly or indirectly in the fulfillment callback of its own 'then' method.": {
194+
"Type is referenced directly or indirectly in the fulfillment callback of its own 'then' method.": {
195195
"category": "Error",
196196
"code": 1062
197197
},
@@ -1611,6 +1611,10 @@
16111611
"category": "Error",
16121612
"code": 2503
16131613
},
1614+
"Type must have a '[Symbol.asyncIterator]()' method that returns an async iterator.": {
1615+
"category": "Error",
1616+
"code": 2504
1617+
},
16141618
"A generator cannot have a 'void' type annotation.": {
16151619
"category": "Error",
16161620
"code": 2505
@@ -1667,6 +1671,10 @@
16671671
"category": "Error",
16681672
"code": 2518
16691673
},
1674+
"An async iterator must have a 'next()' method.": {
1675+
"category": "Error",
1676+
"code": 2519
1677+
},
16701678
"Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions.": {
16711679
"category": "Error",
16721680
"code": 2520
@@ -1759,6 +1767,10 @@
17591767
"category": "Error",
17601768
"code": 2542
17611769
},
1770+
"The type returned by the 'next()' method of an async iterator must be a promise for a type with a 'value' property.": {
1771+
"category": "Error",
1772+
"code": 2543
1773+
},
17621774
"JSX element attributes type '{0}' may not be a union type.": {
17631775
"category": "Error",
17641776
"code": 2600

src/compiler/transformers/es2017.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ namespace ts {
133133
* @param node The method node.
134134
*/
135135
function visitMethodDeclaration(node: MethodDeclaration) {
136-
if (!isAsyncFunctionLike(node)) {
136+
if (!isAsyncFunction(node)) {
137137
return node;
138138
}
139139
const method = createMethod(
@@ -166,7 +166,7 @@ namespace ts {
166166
* @param node The function node.
167167
*/
168168
function visitFunctionDeclaration(node: FunctionDeclaration): VisitResult<Statement> {
169-
if (!isAsyncFunctionLike(node)) {
169+
if (!isAsyncFunction(node)) {
170170
return node;
171171
}
172172
const func = createFunctionDeclaration(
@@ -194,7 +194,7 @@ namespace ts {
194194
* @param node The function expression node.
195195
*/
196196
function visitFunctionExpression(node: FunctionExpression): Expression {
197-
if (!isAsyncFunctionLike(node)) {
197+
if (!isAsyncFunction(node)) {
198198
return node;
199199
}
200200
if (nodeIsMissing(node.body)) {
@@ -223,7 +223,7 @@ namespace ts {
223223
* - The node is marked async
224224
*/
225225
function visitArrowFunction(node: ArrowFunction) {
226-
if (!isAsyncFunctionLike(node)) {
226+
if (!isAsyncFunction(node)) {
227227
return node;
228228
}
229229
const func = createArrowFunction(

src/compiler/transformers/ts.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1670,7 +1670,7 @@ namespace ts {
16701670
if (isFunctionLike(node) && node.type) {
16711671
return serializeTypeNode(node.type);
16721672
}
1673-
else if (isAsyncFunctionLike(node)) {
1673+
else if (isAsyncFunction(node)) {
16741674
return createIdentifier("Promise");
16751675
}
16761676

src/compiler/types.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2862,8 +2862,16 @@ namespace ts {
28622862
// Just a place to cache element types of iterables and iterators
28632863
/* @internal */
28642864
export interface IterableOrIteratorType extends ObjectType, UnionType {
2865-
iterableElementType?: Type;
2866-
iteratorElementType?: Type;
2865+
iteratedTypeOfIterable?: Type;
2866+
iteratedTypeOfIterator?: Type;
2867+
iteratedTypeOfAsyncIterable?: Type;
2868+
iteratedTypeOfAsyncIterator?: Type;
2869+
}
2870+
2871+
/* @internal */
2872+
export interface PromiseOrAwaitableType extends ObjectType, UnionType {
2873+
promisedTypeOfPromise?: Type;
2874+
awaitedTypeOfType?: Type;
28672875
}
28682876

28692877
// Type parameters (TypeFlags.TypeParameter)

src/compiler/utilities.ts

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,8 +1855,50 @@ namespace ts {
18551855
return SyntaxKind.FirstTriviaToken <= token && token <= SyntaxKind.LastTriviaToken;
18561856
}
18571857

1858-
export function isAsyncFunctionLike(node: Node): boolean {
1859-
return isFunctionLike(node) && hasModifier(node, ModifierFlags.Async) && !isAccessor(node);
1858+
export const enum FunctionFlags {
1859+
Normal = 0,
1860+
Generator = 1 << 0,
1861+
Async = 1 << 1,
1862+
AsyncGenerator = Async | Generator,
1863+
Invalid = 1 << 2,
1864+
InvalidGenerator = Generator | Invalid,
1865+
}
1866+
1867+
export function getFunctionFlags(node: FunctionLikeDeclaration) {
1868+
let flags = FunctionFlags.Normal;
1869+
switch (node.kind) {
1870+
case SyntaxKind.FunctionDeclaration:
1871+
case SyntaxKind.FunctionExpression:
1872+
case SyntaxKind.MethodDeclaration:
1873+
if (node.asteriskToken) {
1874+
flags |= FunctionFlags.Generator;
1875+
}
1876+
// fall through
1877+
case SyntaxKind.ArrowFunction:
1878+
if (hasModifier(node, ModifierFlags.Async)) {
1879+
flags |= FunctionFlags.Async;
1880+
}
1881+
break;
1882+
}
1883+
1884+
if (!node.body) {
1885+
flags |= FunctionFlags.Invalid;
1886+
}
1887+
1888+
return flags;
1889+
}
1890+
1891+
export function isAsyncFunction(node: Node): boolean {
1892+
switch (node.kind) {
1893+
case SyntaxKind.FunctionDeclaration:
1894+
case SyntaxKind.FunctionExpression:
1895+
case SyntaxKind.ArrowFunction:
1896+
case SyntaxKind.MethodDeclaration:
1897+
return (<FunctionLikeDeclaration>node).body !== undefined
1898+
&& (<FunctionLikeDeclaration>node).asteriskToken === undefined
1899+
&& hasModifier(node, ModifierFlags.Async);
1900+
}
1901+
return false;
18601902
}
18611903

18621904
export function isStringOrNumericLiteral(kind: SyntaxKind): boolean {

src/lib/es2017.asynciterable.d.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// <reference path="lib.es2015.symbol.d.ts" />
2+
3+
interface SymbolConstructor {
4+
/**
5+
* A method that returns the default async iterator for an object. Called by the semantics of
6+
* the for-await-of statement.
7+
*/
8+
readonly asyncIterator: symbol;
9+
}
10+
11+
interface AsyncIterator<T> {
12+
next(value?: any): Promise<IteratorResult<T>>;
13+
return?(value?: any): Promise<IteratorResult<T>>;
14+
throw?(e?: any): Promise<IteratorResult<T>>;
15+
}
16+
17+
interface AsyncIterable<T> {
18+
[Symbol.asyncIterator](): AsyncIterator<T>;
19+
}
20+
21+
interface AsyncIterableIterator<T> extends AsyncIterator<T> {
22+
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
23+
}

src/lib/es2017.d.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/// <reference path="lib.es2016.d.ts" />
22
/// <reference path="lib.es2017.object.d.ts" />
3-
/// <reference path="lib.es2017.sharedmemory.d.ts" />
3+
/// <reference path="lib.es2017.sharedmemory.d.ts" />
4+
/// <reference path="lib.es2017.asynciterable.d.ts" />

0 commit comments

Comments
 (0)