function visitVariableStatement()

in src/jsdoc_transformer.ts [771:847]


      function visitVariableStatement(varStmt: ts.VariableStatement): ts.Statement[] {
        const stmts: ts.Statement[] = [];

        // "const", "let", etc are stored in node flags on the declarationList.
        const flags = ts.getCombinedNodeFlags(varStmt.declarationList);

        let tags: jsdoc.Tag[]|null =
            moduleTypeTranslator.getJSDoc(varStmt, /* reportWarnings */ true);
        const leading = ts.getSyntheticLeadingComments(varStmt);
        if (leading) {
          // Attach non-JSDoc comments to a not emitted statement.
          const commentHolder = ts.factory.createNotEmittedStatement(varStmt);
          ts.setSyntheticLeadingComments(commentHolder, leading.filter(c => c.text[0] !== '*'));
          stmts.push(commentHolder);
        }

        for (const decl of varStmt.declarationList.declarations) {
          const localTags: jsdoc.Tag[] = [];
          if (tags) {
            // Add any tags and docs preceding the entire statement to the first variable.
            localTags.push(...tags);
            tags = null;
          }
          // Add an @type for plain identifiers, but not for bindings patterns (i.e. object or array
          // destructuring - those do not have a syntax in Closure) or @defines, which already
          // declare their type.
          if (ts.isIdentifier(decl.name)) {
            // For variables that are initialized and use a type marked as unknown, do not emit a
            // type at all. Closure Compiler might be able to infer a better type from the
            // initializer than the `?` the code below would emit.
            // TODO(martinprobst): consider doing this for all types that get emitted as ?, not just
            // for marked ones.
            const initializersMarkedAsUnknown =
                !!decl.initializer && moduleTypeTranslator.isAlwaysUnknownSymbol(decl);
            if (!initializersMarkedAsUnknown) {
              // getOriginalNode(decl) is required because the type checker cannot type check
              // synthesized nodes.
              const typeStr = moduleTypeTranslator.typeToClosure(ts.getOriginalNode(decl));
              // If @define is present then add the type to it, rather than adding a normal @type.
              const defineTag = localTags.find(({tagName}) => tagName === 'define');
              if (defineTag) {
                defineTag.type = typeStr;
              } else {
                localTags.push({tagName: 'type', type: typeStr});
              }
            }
          } else if (ts.isArrayBindingPattern(decl.name)) {
            const aliases: Array<[ts.Identifier, ts.Identifier]> = [];
            const updatedBinding = renameArrayBindings(decl.name, aliases);
            if (updatedBinding && aliases.length > 0) {
              const declVisited = ts.visitNode(decl, visitor);
              const newDecl = ts.factory.updateVariableDeclaration(
                  declVisited, updatedBinding, declVisited.exclamationToken,
                  declVisited.type, declVisited.initializer);
              const newStmt = ts.factory.createVariableStatement(
                  varStmt.modifiers,
                  ts.factory.createVariableDeclarationList([newDecl], flags));
              if (localTags.length) {
                addCommentOn(
                    newStmt, localTags, jsdoc.TAGS_CONFLICTING_WITH_TYPE);
              }
              stmts.push(newStmt);
              stmts.push(...createArrayBindingAliases(
                  varStmt.declarationList.flags, aliases));
              continue;
            }
          }
          const newDecl = ts.visitNode(decl, visitor);
          const newStmt = ts.factory.createVariableStatement(
              varStmt.modifiers,
              ts.factory.createVariableDeclarationList([newDecl], flags));
          if (localTags.length) addCommentOn(newStmt, localTags, jsdoc.TAGS_CONFLICTING_WITH_TYPE);
          stmts.push(newStmt);
        }

        return stmts;
      }