in src/jsdoc_transformer.ts [623:754]
function visitFunctionLikeDeclaration<T extends ts.FunctionLikeDeclaration>(fnDecl: T): T {
if (!fnDecl.body) {
// Two cases: abstract methods and overloaded methods/functions.
// Abstract methods are handled in emitTypeAnnotationsHandler.
// Overloads are union-ized into the shared type in FunctionType.
return ts.visitEachChild(fnDecl, visitor, context);
}
const extraTags = [];
if (hasExportingDecorator(fnDecl, typeChecker)) extraTags.push({tagName: 'export'});
const {tags, thisReturnType} =
moduleTypeTranslator.getFunctionTypeJSDoc([fnDecl], extraTags);
// async functions when down-leveled access `this` to pass it to
// tslib.__awaiter. Closure wants to know the type of 'this' for that.
// The type is known in many contexts (e.g. methods, arrow functions)
// per the normal rules (e.g. looking at parent nodes and @this tags)
// but if the search bottoms out at a function scope, then Closure
// warns that 'this' is unknown.
// Because we have already checked the type of 'this', we are ok to just
// suppress in that case. We do so by stuffing a @this on any function
// where it might be needed; it's harmless to overapproximate.
const isDownlevellingAsync =
tsOptions.target !== undefined && tsOptions.target <= ts.ScriptTarget.ES2018;
const isFunction = fnDecl.kind === ts.SyntaxKind.FunctionDeclaration;
const hasExistingThisTag = tags.some(t => t.tagName === 'this');
if (isDownlevellingAsync && isFunction && !hasExistingThisTag && containsAsync(fnDecl)) {
tags.push({tagName: 'this', type: '*'});
}
const mjsdoc = moduleTypeTranslator.getMutableJSDoc(fnDecl);
mjsdoc.tags = tags;
mjsdoc.updateComment();
const contextThisTypeBackup = contextThisType;
// Arrow functions retain their context `this` type. All others reset the this type to
// either none (if not specified) or the type given in a fn(this: T, ...) declaration.
if (!ts.isArrowFunction(fnDecl)) contextThisType = thisReturnType;
fnDecl = ts.visitEachChild(fnDecl, visitor, context);
contextThisType = contextThisTypeBackup;
if (!fnDecl.body) {
// abstract functions do not need aliasing of their destructured
// arguments.
return fnDecl;
}
// Alias destructured function parameters for more precise types.
const bindingAliases: Array<[ts.Identifier, ts.Identifier]> = [];
const updatedParams = [];
let hasUpdatedParams = false;
for (const param of fnDecl.parameters) {
if (!ts.isArrayBindingPattern(param.name)) {
updatedParams.push(param);
continue;
}
const updatedParamName =
renameArrayBindings(param.name, bindingAliases);
if (!updatedParamName) {
updatedParams.push(param);
continue;
}
hasUpdatedParams = true;
updatedParams.push(ts.factory.updateParameterDeclaration(
param, param.decorators, param.modifiers, param.dotDotDotToken,
updatedParamName, param.questionToken, param.type,
param.initializer));
}
if (!hasUpdatedParams || bindingAliases.length === 0) return fnDecl;
let body = fnDecl.body;
const stmts: ts.Statement[] =
createArrayBindingAliases(ts.NodeFlags.Let, bindingAliases);
if (!ts.isBlock(body)) {
stmts.push(ts.factory.createReturnStatement(
// Use ( parens ) to protect the return statement against
// automatic semicolon insertion.
ts.factory.createParenthesizedExpression(body)));
body = ts.factory.createBlock(stmts, true);
} else {
stmts.push(...body.statements);
body = ts.factory.updateBlock(body, stmts);
}
switch (fnDecl.kind) {
case ts.SyntaxKind.FunctionDeclaration:
fnDecl =
ts.factory.updateFunctionDeclaration(
fnDecl, fnDecl.decorators, fnDecl.modifiers,
fnDecl.asteriskToken, fnDecl.name, fnDecl.typeParameters,
updatedParams, fnDecl.type, body) as T;
break;
case ts.SyntaxKind.MethodDeclaration:
fnDecl = ts.factory.updateMethodDeclaration(
fnDecl, fnDecl.decorators, fnDecl.modifiers,
fnDecl.asteriskToken, fnDecl.name,
fnDecl.questionToken, fnDecl.typeParameters,
updatedParams, fnDecl.type, body) as T;
break;
case ts.SyntaxKind.SetAccessor:
fnDecl = ts.factory.updateSetAccessorDeclaration(
fnDecl, fnDecl.decorators, fnDecl.modifiers,
fnDecl.name, updatedParams, body) as T;
break;
case ts.SyntaxKind.Constructor:
fnDecl = ts.factory.updateConstructorDeclaration(
fnDecl, fnDecl.decorators, fnDecl.modifiers,
updatedParams, body) as T;
break;
case ts.SyntaxKind.FunctionExpression:
fnDecl = ts.factory.updateFunctionExpression(
fnDecl, fnDecl.modifiers, fnDecl.asteriskToken,
fnDecl.name, fnDecl.typeParameters, updatedParams,
fnDecl.type, body) as T;
break;
case ts.SyntaxKind.ArrowFunction:
fnDecl = ts.factory.updateArrowFunction(
fnDecl, fnDecl.modifiers, fnDecl.name, updatedParams,
fnDecl.type, fnDecl.equalsGreaterThanToken, body) as T;
break;
case ts.SyntaxKind.GetAccessor:
moduleTypeTranslator.error(
fnDecl, `get accessors cannot have parameters`);
break;
default:
moduleTypeTranslator.error(
fnDecl, `unexpected function like declaration`);
break;
}
return fnDecl;
}