2

I am trying to use the Typescript Compiler API and local file system to retrieve the exported object of a typescript config file and use it in node.js.

Given a simplified example like the below:

// test.config.ts

type Config = {
  hello: string;
};

const config: Config = {
  hello: "world",
};

export default config;

In another file how would I use the Compiler API to extract the exported object into a variable so I can use it in js?

//another-file.js

const source = "./test.config.ts"

let exportedObject = /* some Compiler API function(s) to retrieve exported object from 'test.config.ts' */

console.log(exportedObject.hello)
// logs "world"

I've been able to load a program and source file - but I'm a bit lost on what do do next. Any documentation/resources would be greatly appreciated!

//another-file.js
const source = "./test.config.ts";

const program = ts.createProgram([source]);
const sourceFile = program.getSourceFile(source);

1 Answer 1

5

If you want to get the exports of the module, you can use the TypeChecker#getExportsOfModule method:

const checker = program.getTypeChecker();

const sourceFileSymbol = checker.getSymbolAtLocation(sourceFile)!;
const exports = checker.getExportsOfModule(sourceFileSymbol);

Then from these exports you can get/check if there's a default export, which will be a ts.Symbol:

const defaultExportSymbol = exports.find(e => e.escapedName === "default")!;

From that, you can get its type, which will allow you to find its properties:

const defaultExportType = checker.getTypeOfSymbolAtLocation(
  defaultExportSymbol,
  defaultExportSymbol.declarations![0],
);

for (const prop of defaultExportType.getProperties()) {
  const propType = checker.getTypeOfSymbolAtLocation(prop, prop.declarations![0]);
  console.log(prop.name); // hello
  console.log(checker.typeToString(propType)); // string
}

If something is not working, try checking console.log(ts.getPreEmitDiagnostics(program)) to ensure there are no diagnostics in the program.

Getting config

To get config, you will need to get the aliased symbol of the default export:

const configSymbol = checker.getAliasedSymbol(defaultExport);

From that, you can get the variable declaration along with the initializer's object literal expression which when traversing the properties will give you the value:

const configDecl = configSymbol.declarations![0] as ts.VariableDeclaration;
const objLit = configDecl.initializer as ts.ObjectLiteralExpression;

for (const prop of objLit.properties) {
  console.log(prop.name!.getText()); // hello
  console.log((prop as ts.PropertyAssignment).initializer.getText()); // "world"
}

Obviously I'm doing a ton of assertions in this code... the actual code should be written to be more flexible and handle more scenarios.

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks @David - this works to well to get the object key and type - I had to switch type.getProperties() to defaultExportType.getProperties(). Is there any way to get the actual value of the object literal properties aside from key and type? For instance getting "world" in this example?
Yes, it's possible in this case because it's statically analyzable. I updated the answer to show how.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.