Is it possible to get comments as nodes in the AST using the typescript compiler API?

It's not possible to get comments as nodes but it is still possible to get comments from the source file. The function to use is getLeadingCommentRanges(text: string, pos: number).

I used it like this:

for(var sourceFile of program.getSourceFiles()){
        ts.forEachChild(sourceFile, visit);
    }

function visit(node: ts.Node){
    const commentRanges = ts.getLeadingCommentRanges(
        sourceFile.getFullText(), 
        node.getFullStart());
    if (commentRange?.length)
        const commentStrings:string[] = 
          commentRanges.map(r=>sourceFile.getFullText().slice(r.pos,r.end))
}

NOTE: sourceFile.getFullText() does not skip leading comments, and sourceFile.getText() does skip leading comments. Using .getText in the above usage case is apparently a common source of errors.


In case you use jsDoc-style comments, e.g.

export const myConst = {
  /**
   * My property description
   */
  myProp: 'test prop',
};

it's possible to retrieve a comment content during the tree traverse.

For example extractWithComment will return

{
  name: 'myProp',
  comment: 'My property description',
  type: 'string'
}

import * as ts from 'typescript';

export function extractWithComment(fileNames: string[], options: ts.CompilerOptions): void {
  const program = ts.createProgram(fileNames, options);
  const checker: ts.TypeChecker = program.getTypeChecker();

  for (const sourceFile of program.getSourceFiles()) {
    if (!sourceFile.isDeclarationFile) {
      ts.forEachChild(sourceFile, visit);
    }
  }

  function visit(node: ts.Node) {
    const count = node.getChildCount()

    if (count > 0) {
      ts.forEachChild(node, visit);
    }

    if (ts.isPropertyAssignment(node) && node.name) {
      const symbol = checker.getSymbolAtLocation(node.name);
      if (symbol) {
        return serializeSymbol(symbol)
      }
    }
  }

  function serializeSymbol(symbol: ts.Symbol) {
    return {
      name: symbol.getName(),
      comment: ts.displayPartsToString(symbol.getDocumentationComment(checker)),
      type: checker.typeToString(
        checker.getTypeOfSymbolAtLocation(symbol, symbol.valueDeclaration!)
      ),
    };
  }
}