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