Get resolved type (and jsdoc tags) of variable initializer using TypeScript compiler API

Viewed 463

Starting from the ts.Node for the initializer of p.prop1 below, is there a simple way to get the resolved type info (including doc tags/comment) for Props.prop1 using a type checker?

interface Props {
  /**
   * Doc comment
   * @tag
   */
  prop1: string;
}

const p: Props = { prop1: 'hello' };
//                 ^^^^^

I'm aware of APIs like getTypeAtLocation, getDeclaredTypeOfSymbol, getApparentType, and getPropertyOfType, but haven't been able to figure out how to combine them (or what other APIs might be needed) to do what I want. Calling typeChecker.getSymbolAtLocation(node) on the prop1 initializer node returns a symbol which doesn't have the full type information.

(Ultimately what I want is the JSDoc tags, which once you have the right ts.Symbol can be acquired with symbol.getJsDocTags().)

1 Answers

Getting type of prop1 identifier in object literal expression

To get the type, you can use getTypeAtLocation on the type checker:

// prop1 is a variable containing the ts.Node (ts.Identifier) of
// the node you highlighted in your question
const prop1Type = typeChecker.getTypeAtLocation(prop1.name);

// outputs: string
console.log(typeChecker.typeToString(prop1Type));

Getting its JS doc tags

I'm honestly not sure what the correct way to get the JS doc tags are, but I've found it works to get the object literal expression's contextual type (I think that means it gets the definition type) then from there get the property of prop1:

const prop1Symbol = typeChecker.getSymbolAtLocation(prop1Symbol)!;
const propsType = typeChecker.getContextualType(prop1.parent)!;

// outputs the tag
console.log(propsType.getProperty(prop1Symbol.getName())!.getJsDocTags());

Full Example

import { createProjectSync, ts } from "@ts-morph/bootstrap";

// setup
const project = createProjectSync({ useInMemoryFileSystem: true });
const sourceFile = project.createSourceFile("file.ts", `
interface Props {
  /**
   * Doc comment
   * @tag
   */
  prop1: string;
}
const p: Props = { prop1: 'hello' };
`);
const typeChecker = project.createProgram().getTypeChecker();

// Get the prop1 identifier in the `p` variable declaration's initializer's
// object literal expression
const varStmt = sourceFile.statements.find(ts.isVariableStatement)!;
const varDecl = varStmt.declarationList.declarations[0];
const varDeclObjLit = varDecl.initializer as ts.ObjectLiteralExpression;
const prop1 = varDeclObjLit.properties[0] as ts.PropertyAssignment;

// outputs: string
const prop1Type = typeChecker.getTypeAtLocation(prop1.name);
console.log(typeChecker.typeToString(prop1Type));

// outputs the js doc tag
const prop1Symbol = typeChecker.getSymbolAtLocation(prop1.name)!;
const propsType = typeChecker.getContextualType(prop1.parent)!;
console.log(propsType.getProperty(prop1Symbol.getName())!.getJsDocTags());
Related