我正在尝试以编程方式为第三方库创建的定义创建一个typescript AST,并使用该AST打印我们在语法中声明的类型定义集(.d.ts文件)。库。
依靠documentation for the compiler API,并挖掘打字稿编译器的源文件,我相信我有一个大多数正确的AST。但是,我还没有找到一种方法来输出此AST的类型定义(对应于.d.ts文件),只打印AST作为打字稿程序。
我的问题实际上是两部分:
答案 0 :(得分:0)
复制和更改TypeScript源的ts.transpileModule
函数中所做的操作以仅输出声明文件可能是最简单的。
例如:
export function transpileModuleDeclaration(sourceFile: ts.SourceFile, transpileOptions: ts.TranspileOptions) {
const diagnostics: ts.Diagnostic[] = [];
const options = transpileOptions.compilerOptions != null ? { ...transpileOptions.compilerOptions } : ts.getDefaultCompilerOptions();
options.isolatedModules = true;
// in this case we only want to emit the declaration file
options.emitDeclarationOnly = true;
options.declaration = true;
// this does not write anything to disk so there is no need to verify that there are no conflicts between input and output paths.
options.suppressOutputPathCheck = true;
// Filename can be non-ts file.
options.allowNonTsExtensions = true;
// We are not returning a sourceFile for lib file when asked by the program,
// so pass --noLib to avoid reporting a file not found error.
options.noLib = true;
// Clear out other settings that would not be used in transpiling this module
options.lib = undefined;
options.types = undefined;
options.noEmit = undefined;
options.noEmitOnError = undefined;
options.paths = undefined;
options.rootDirs = undefined;
options.composite = undefined;
options.declarationDir = undefined;
options.out = undefined;
options.outFile = undefined;
// We are not doing a full typecheck, we are not resolving the whole context,
// so pass --noResolve to avoid reporting missing file errors.
options.noResolve = true;
// if jsx is specified then treat file as .tsx
const inputFileName = transpileOptions.fileName || (options.jsx ? "module.tsx" : "module.ts");
if (transpileOptions.moduleName) {
sourceFile.moduleName = transpileOptions.moduleName;
}
// Output
let outputText: string | undefined;
// Create a compilerHost object to allow the compiler to read and write files
const compilerHost: ts.CompilerHost = {
getSourceFile: (fileName) => fileName === inputFileName.replace(/\\/g, "/") ? sourceFile : undefined,
writeFile: (name, text) => {
outputText = text;
},
getDefaultLibFileName: () => "lib.d.ts",
useCaseSensitiveFileNames: () => false,
getCanonicalFileName: fileName => fileName,
getCurrentDirectory: () => "",
getNewLine: () => options.newLine == null || options.newLine === ts.NewLineKind.LineFeed ? "\n" : "\r\n",
fileExists: fileName => fileName === inputFileName,
readFile: () => "",
directoryExists: () => true,
getDirectories: () => []
};
const program = ts.createProgram([inputFileName], options, compilerHost);
if (transpileOptions.reportDiagnostics) {
diagnostics.push(...program.getSyntacticDiagnostics(sourceFile));
diagnostics.push(...program.getOptionsDiagnostics());
}
// Emit
program.emit(/*targetSourceFile*/ undefined, /*writeFile*/ undefined, /*cancellationToken*/ undefined, /*emitOnlyDtsFiles*/ undefined, transpileOptions.transformers);
if (outputText === undefined)
throw new Error("Output generation failed.");
return { outputText, diagnostics };
}
然后使用它:
const file = ts.createSourceFile("test.ts", `function myFunc(a: number, b: number) {
return a + b;
}`, ts.ScriptTarget.Latest, true);
const result = transpileModuleDeclaration(file, {});
console.log(result.outputText);
输出:
declare function myFunc(a: number, b: number): number;