Correct way of getting type for a variable declaration in a typescript AST?

2k Views Asked by At

Took a look at the declarationEmitter and for variable declarations, it has the function #emitVariableDeclaration which eventually calls #writeTypeOfDeclaration. This code does what is says---it takes a variable declaration and prints the variable and its type---this is exactly what I want to do.

The problem is that when I replicate this code, the VariableDeclaration node has no symbol property...and thus, the type is always "any". Is there a missing step to initialize "symbols"?

//sample input filecontents
export const foo = '123'

//mycode.js
const ast = ts.createSourceFile(filename, filecontents, ts.ScriptTarget.ES6, true))
const program = ts.createProgram([filename], {});
const typeChecker = program.getDiagnosticsProducingTypeChecker()
const emitResolver = typeChecker.getEmitResolver(filename)
// variableDeclarationNode --- can be obtained using ts.forEachChild and finding node with kind === ts.SyntaxKind.VariableDeclaration
// writer --- an object implementing the SymbolWriter interface that just concatenates to a result string
emitResolver.writeTypeOfDeclaration(variableDeclarationNode, undefined, undefined, writer)

//declarationEmitter.ts
function writeTypeOfDeclaration(
      declaration: AccessorDeclaration | VariableLikeDeclaration, 
      type: TypeNode, 
      getSymbolAccessibilityDiagnostic: GetSymbolAccessibilityDiagnostic) {
   //...
   resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, TypeFormatFlags.UseTypeOfFunction, writer);
}

//`checker.ts`
function writeTypeOfDeclaration(
    declaration: AccessorDeclaration | VariableLikeDeclaration,       
    enclosingDeclaration: Node, 
    flags: TypeFormatFlags, 
    writer: SymbolWriter) {

    // Get type of the symbol if this is the valid symbol otherwise get type at location
    const symbol = getSymbolOfNode(declaration);
    const type = symbol && !(symbol.flags & (SymbolFlags.TypeLiteral | SymbolFlags.Signature))
        ? getTypeOfSymbol(symbol)
        : unknownType;

    // ....
}

function getMergedSymbol(symbol: Symbol): Symbol {
    let merged: Symbol;
    return symbol && symbol.mergeId && (merged = mergedSymbols[symbol.mergeId]) ? merged : symbol;
}

function getSymbolOfNode(node: Node): Symbol {
    return getMergedSymbol(node.symbol);
}
2

There are 2 best solutions below

2
basarat On

Is there a missing step to initialize "symbols"?

This is the job of the binder.

Some docs : https://basarat.gitbooks.io/typescript/content/docs/compiler/binder.html

2
U Avalos On

It turns out I was using the wrong AST. In the question, I'm using the AST that doesn't have type. You can get the AST with type injected with the program. The (more) correct solution is:

const program = ts.createProgram(filename, {target: ts.ScriptTarget.ES6, module: ts.ModuleKind.ES6});
const typechecker = program.getDiagnosticsProducingTypeChecker()
const emitResolver = typeChecker.getEmitResolver(filename)))

// THIS IS HOW TO GET AN AST WITH TYPE (Yes, it's called a "SourceFile")
const ast = program.getSourceFile(filename)

// variableDeclarationNode --- can be obtained using ts.forEachChild and finding node with kind === ts.SyntaxKind.VariableDeclaration
// writer --- an object implementing the SymbolWriter interface that just concatenates to a result string
emitResolver.writeTypeOfDeclaration(variableDeclarationNode, undefined, undefined, writer)