Typescript 编译器 API - 提取导出的 Typescript 对象

Typescript Compiler API - Extract Exported Typescript Object

我正在尝试使用 Typescript 编译器 API 和本地文件系统来检索 typescript 配置文件的导出对象并在 node.js.

中使用它

给出一个如下所示的简化示例:

// test.config.ts

type Config = {
  hello: string;
};

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

export default config;

在另一个文件中,我如何使用编译器 API 将导出的对象提取到变量中,以便我可以在 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"

我已经能够加载程序和源文件 - 但我有点不知道下一步该做什么。任何 documentation/resources 将不胜感激!

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

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

如果想获取模块的导出,可以使用TypeChecker#getExportsOfModule方法:

const checker = program.getTypeChecker();

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

如果有默认导出,您可以从这些导出中 get/check,这将是 ts.Symbol:

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

从那里,你可以得到它的类型,这将允许你找到它的属性:

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
}

如果出现问题,请尝试检查 console.log(ts.getPreEmitDiagnostics(program)) 以确保程序中没有诊断。

获取config

要获取config,您需要获取默认导出的别名符号:

const configSymbol = checker.getAliasedSymbol(defaultExport);

由此,您可以获得变量声明以及初始化程序的对象文字表达式,在遍历属性时将为您提供值:

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"
}

显然我在这段代码中做了很多断言...实际代码应该写得更灵活并处理更多场景。