Skip to content

Commit

Permalink
Add explicit @this {*} annotation to some more async fns. (#1064)
Browse files Browse the repository at this point in the history
Because the async downleveler emits 'this' for all async methods we have
to explicitly annotate the 'this' usage so that closure does not
complain.

Technically for static methods the right annotation would be `@this
{typeof C}` but for now the already existing `*` should suffice.

This also add @this {*} for arrow expressions and functions.
  • Loading branch information
rkirov committed Sep 9, 2019
1 parent 7a4a955 commit 40a006a
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 6 deletions.
24 changes: 18 additions & 6 deletions src/jsdoc_transformer.ts
Expand Up @@ -595,12 +595,24 @@ export function jsdocTransformer(
}
}

// top-level async functions when down-leveled access `this` to pass it to
// tslib.__awaiter. Closure requires a @this tag for that.
if ((tsOptions.target !== undefined && tsOptions.target <= ts.ScriptTarget.ES2015) &&
transformerUtil.hasModifierFlag(fnDecl, ts.ModifierFlags.Async) &&
// Methods/getters/setters/ctors already have an implicit this.
fnDecl.kind === ts.SyntaxKind.FunctionDeclaration &&
// All async functions when down-leveled access `this` to pass it to
// tslib.__awaiter. Closure requires a @this tag for that in some
// situations.
const fnDeclNeedsExplicitThis =
// Instance methods already have an implicit this. So we only care
// for function declarations/expressions and arrow fns.
fnDecl.kind === ts.SyntaxKind.FunctionDeclaration ||
fnDecl.kind === ts.SyntaxKind.FunctionExpression ||
fnDecl.kind === ts.SyntaxKind.ArrowFunction ||
// static methods also need explicit this
// TODO(rado): The correct emit for static methods would be
// {typeof C} where C is the enclosing class.
(fnDecl.kind === ts.SyntaxKind.MethodDeclaration &&
transformerUtil.hasModifierFlag(fnDecl, ts.ModifierFlags.Static));

if (transformerUtil.hasModifierFlag(fnDecl, ts.ModifierFlags.Async) &&
(tsOptions.target !== undefined && tsOptions.target <= ts.ScriptTarget.ES2015) &&
fnDeclNeedsExplicitThis &&
// There might be an explicit `this: T` type.
!tags.some(t => t.tagName === 'this')) {
tags.push({tagName: 'this', type: '*'});
Expand Down
30 changes: 30 additions & 0 deletions test_files/async_functions/async_functions.js
Expand Up @@ -53,6 +53,7 @@ function asyncTopLevelFunctionWithThisType(param) {
const asyncTopLevelArrowFunction = (/**
* @param {string} param
* @return {!Promise<string>}
* @this {*}
*/
(param) => tslib_1.__awaiter(this, void 0, void 0, function* () {
/** @type {!Promise<string>} */
Expand Down Expand Up @@ -90,6 +91,7 @@ class Container {
const asyncArrowFunctionInMethod = (/**
* @param {string} param
* @return {!Promise<string>}
* @this {*}
*/
(param) => tslib_1.__awaiter(this, void 0, void 0, function* () {
/** @type {!Promise<string>} */
Expand Down Expand Up @@ -127,8 +129,36 @@ class Container {
});
}
}
/**
* @return {!Promise<string>}
* @this {*}
*/
static asyncStaticMethod() {
return tslib_1.__awaiter(this, void 0, void 0, /** @this {!Container} */ function* () {
/** @type {string} */
const s = yield asyncTopLevelFunction('x');
return s + this.staticField;
});
}
}
Container.staticField = 's';
if (false) {
/** @type {string} */
Container.staticField;
/** @type {string} */
Container.prototype.field;
}
/** @type {function(): !Promise<void>} */
const asyncFnExpression = (/**
* @return {!Promise<void>}
* @this {*}
*/
function f() {
return tslib_1.__awaiter(this, void 0, void 0, function* () { });
});
/** @type {function(): !Promise<void>} */
const asyncArrowFn = (/**
* @return {!Promise<void>}
* @this {*}
*/
() => tslib_1.__awaiter(this, void 0, void 0, function* () { }));
10 changes: 10 additions & 0 deletions test_files/async_functions/async_functions.ts
Expand Up @@ -48,4 +48,14 @@ class Container {
return s + this.field;
}
}

static staticField = 's';

static async asyncStaticMethod() {
const s = await asyncTopLevelFunction('x');
return s + this.staticField;
}
}

const asyncFnExpression = async function f() {};
const asyncArrowFn = async () => {};

0 comments on commit 40a006a

Please sign in to comment.