Jose Ch.
Jose Ch.

Reputation: 3896

Typescript compiler, how to navigate to the definition of a symbol?

Overall goal is to extract type information from exported modules. However, it seems navigating to the actual definition is required, example:

// fileA
function Foo() {...}
export default Foo
// fileB
export default function Foo() {...}

Extracting information:

// Analyzing file A
const defaultExportSymbolFileA = fileSymbol?.exports.get('default');
const type = typeChecker.getTypeOfSymbolAtLocation(defaultExportSymbolFileA, defaultExportSymbolFileA.valueDeclaration); // won't work

// Analyzing file B
const defaultExportSymbolFileB = fileSymbol?.exports.get('default');
const type = typeChecker.getTypeOfSymbolAtLocation(defaultExportSymbolFileB, defaultExportSymbolFileB.valueDeclaration); // works

Thank you!

Upvotes: 1

Views: 883

Answers (1)

David Sherret
David Sherret

Reputation: 106660

From my experience, you will need to get the aliased symbol if the symbol is an alias in this case.

function getDefaultExportDeclaration(fileSymbol: ts.Symbol) {
    if (fileSymbol.exports == null)
        return undefined;
    const defaultSymbol = fileSymbol.exports.get(ts.escapeLeadingUnderscores("default"));
    return defaultSymbol == null
        ? undefined
        : getAliasedSymbolIfNecessary(defaultSymbol).valueDeclaration;
}

function getAliasedSymbolIfNecessary(symbol: ts.Symbol) {
    if ((symbol.flags & ts.SymbolFlags.Alias) !== 0)
        return typeChecker.getAliasedSymbol(symbol);
    return symbol;
}

For example, with the following code that uses this function:

// setup (this is my library that provides an easier setup with the compiler api)
import { Project, ts } from "@ts-morph/bootstrap";

const project = new Project();
const fileA = project.createSourceFile("fileA.ts", `function Foo() {} export default Foo;`);
const fileB = project.createSourceFile("fileB.ts", `export default function Foo() {}`);
const fileC = project.createSourceFile("fileC.ts", `import Foo from "./FileB";
export default Foo;`);

const program = project.createProgram();
const typeChecker = program.getTypeChecker();

// get result and output
console.log(getDefaultExportDeclaration(typeChecker.getSymbolAtLocation(fileA)!)!.getText());
console.log(getDefaultExportDeclaration(typeChecker.getSymbolAtLocation(fileB)!)!.getText());
console.log(getDefaultExportDeclaration(typeChecker.getSymbolAtLocation(fileC)!)!.getText());

The output will be:

function Foo() {}
export default function Foo() {}
export default function Foo() {}

Upvotes: 1

Related Questions