Typescript 如何使用TS编译器API查找变量在另一个文件中的定义位置
鉴于: 如果我在“foo.ts”中有一个Typescript 如何使用TS编译器API查找变量在另一个文件中的定义位置,typescript,typescript-compiler-api,Typescript,Typescript Compiler Api,鉴于: 如果我在“foo.ts”中有一个ts.Symbol用于bar,我如何才能到达“bar.ts”中的bar 理想情况下,TS编译器API将公开一个定义使用链,我可以遍历它来找到定义。不过,我认为没有 所以现在我想: 使用模块说明符“/bar.ts”和当前ts.SourceFile获取表示“bar.ts”的ts.ResolvedModule对象,该对象包含完整的文件路径 执行ts.SourceFile(fullFilePath)以获取“bar.ts”的ts.SourceFile 执行chec
ts.Symbol
用于bar
,我如何才能到达“bar.ts”中的bar
理想情况下,TS编译器API将公开一个定义使用链,我可以遍历它来找到定义。不过,我认为没有
所以现在我想:
- 使用模块说明符“/bar.ts”和当前
获取表示“bar.ts”的ts.SourceFile
对象,该对象包含完整的文件路径ts.ResolvedModule
- 执行
以获取“bar.ts”的ts.SourceFile(fullFilePath)
ts.SourceFile
- 执行checker.getExportsOfModule(symbolForBarDotTs)以从“bar.ts”获取导出并找到具有匹配名称的导出
,不幸的是,只有当主机实现了它,而默认编译器主机没有实现它时,它才可用host.resolveModuleNames
- 使用
。(program.getSourceFile(pathtoo)作为任何).resolvedModules
属性似乎正是我想要的,但它不是公共API的一部分resolvedModules
- 停止使用非私有API
- 停止做太多编译器已经知道如何做的工作 “酒吧”
ts.Symbol
用于bar
,我如何才能到达“bar.ts”中的bar
大部分时间都能用
但是,在以下情况下,它不符合我的要求:
// foo.ts
import { bar } from "./bar"
// bar.ts
export const bar = 3;
TypeChecker#getAliasedSymbol说,“foo.ts”中的
baz
指向“baz.ts”中的bar
,完全跳过了“bar.ts”。这对我来说是行不通的,因为我试图找出,给定一组入口点,.d.ts文件的哪些部分不再需要,并删除不需要的部分。在这种情况下,删除“bar.ts”不是一个好主意。命名导入的符号将有一个关联的“别名符号”,它表示声明。因此,要获取变量声明的符号,可以使用TypeChecker#getAliasedSymbol
方法,然后从中获取声明
例如:
// foo.ts
import { bar } from "./bar"
// bar.ts
export { bar } from "./baz"
// baz.ts
export const bar = 3;
导入声明的命名导入有一个单独的符号,因为这是特定于“foo.ts”文件的符号
更新:获取模块说明符中引用的文件符号
要获取导入或导出声明模块说明符中引用的文件的符号,可以获取模块说明符节点的符号:
const barNamedImportSymbol = typeChecker.getSymbolAtLocation(barNamedImport.name)!;
const barSymbol = typeChecker.getAliasedSymbol(barNamedImportSymbol);
const barDeclaration = barSymbol.declarations[0] as ts.VariableDeclaration;
console.log(barDeclaration.getText(barFile)); // outputs `bar = 3`
从那里,您可以检查其导出的特定名称:
const otherFileSymbol = typeChecker.getSymbolAtLocation(importDeclaration.moduleSpecifier)!;
要解析模块名称,请使用
ts.resolveModuleName
这是签名,来自typescript.d.ts
:
const barSymbol = otherFileSymbol.exports!.get(ts.escapeLeadingUnderscores("bar"))!;
// outputs: export { bar } from "./baz"; in second example above
console.log(barSymbol.declarations[0].parent.parent.getText());
或者,要直接转到符号的原始定义,请参见您提出的简单问题:p.对于编译器API,GH不是更好的地方吗?谢谢!我不确定我是否会将此称为功能请求,除非有人确认我试图实现的功能没有公共API,否则我可能会提出一个问题。如果我没有弄错的话,你可以在GitHub上提出一个问题@TitianCernicova Dragomir是撰稿人之一…@Hereticsmonkey Max Heiber也是撰稿人:)发表了一个问题:感谢您的反馈和建议谢谢!但这并不能100%解决我的问题。我问如何从“/bar”导入{bar}中的“bar”,这个答案中的方法并不总是有效,因为getAliasedSymbol在文件之间跳过。也就是说,如果
foo.ts
正在从“/baz”导出{bar},那么别名符号将位于baz.ts中,而不是位于bar.ts中。这是一个问题,因为我正在进行死代码消除,在本例中需要将bar.ts标记为live。要么这样,要么重写模块说明符,但这也需要了解模块解析。@MaxHeiber您能用一个示例来更新您的问题,说明您的意思吗?我只是回答:“如果我在“foo.ts”中有一个酒吧的ts.Symbol,我怎么能在“bar.ts”中到达酒吧?”“有很多不同的场景需要处理。。。例如,非常抱歉我的耽搁!我已经更新了我的答案,我认为你在寻找什么。谢谢@davidSherret。我选择了正确的答案,但还是选择了使用ts.resolveModuleName,因为这是对代码的一个较小的更改-尽管这两种解决方案似乎都可以工作。@MaxHeiber我认为使用ts.resolveModuleName
在程序主机实现自己的模块解析的情况下不起作用。看见我相信ts.resolveModuleNames
是程序在ProgramHost\resolveModuleNames
未定义时使用的默认模块解析。
resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, redirectedReference: ResolvedProjectReference | undefined, options: CompilerOptions): (ResolvedModule | undefined)[];