function visitFunctionLikeDeclaration()

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;
      }