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);
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.