如何在自定义VSCode扩展中获取JavaScript表达式的类型信息?
核心需求
我正在开发一个VSCode自定义扩展,用来对比HTML自定义属性与JavaScript表达式的类型。目前已通过JSDoc/TypeScript定义+NPM包实现HTML属性类型的获取,但卡在了获取模板字符串中嵌入表达式(如示例里的${page.SomeProperty})的类型上,同时需要支持以下复杂表达式场景:
- 箭头函数:
(args) => result - 函数调用:
identifier.function() - 原始类型(string、number、boolean)
- 复杂类型数组
- 匿名对象
- 匿名对象数组
示例代码:
/** @param {MyPage} page */ export function renderSomePage(page) { return html` <my-component .my-attribute="${page.SomeProperty}"></my-component> ` }
可行实现方案
方案1:利用VSCode内置语言服务(推荐)
VSCode的TypeScript/JavaScript语言服务本身已掌握表达式的类型信息,无需自行解析语法树,可通过VSCode API直接调用:
获取当前文档的语言服务实例
通过vscode.window.activeTextEditor拿到当前编辑器,再调用vscode.languages.getLanguageService获取对应TS/JS语言服务。定位表达式位置
通过正则匹配或AST解析工具(如@babel/parser),找到模板字符串中${}包裹的表达式在文档中的精确起止偏移量。请求类型信息
调用语言服务的getQuickInfoAtPosition方法,传入文档URI和表达式起始位置,返回的QuickInfo对象包含类型的显示文本、类型分类等信息,可直接提取使用。
示例代码片段:
import * as vscode from 'vscode'; import * as ts from 'typescript'; async function getExpressionType(editor: vscode.TextEditor, exprStartPos: number) { const doc = editor.document; const languageService = vscode.languages.getLanguageService({ getTextDocument: (uri) => uri.toString() === doc.uri.toString() ? doc : undefined, getScriptFileNames: () => [doc.uri.fsPath], getScriptVersion: () => '1', getScriptSnapshot: (fileName) => fileName === doc.uri.fsPath ? ts.ScriptSnapshot.fromString(doc.getText()) : undefined, getCurrentDirectory: () => vscode.workspace.rootPath || '', getCompilationSettings: () => ({ allowJs: true, checkJs: true }), getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options) }) as ts.LanguageService; const quickInfo = languageService.getQuickInfoAtPosition(doc.uri.fsPath, exprStartPos); if (quickInfo) { return quickInfo.displayParts?.map(part => part.text).join(''); } return null; }
方案2:直接使用TypeScript Compiler API
若需要更底层的类型结构(如泛型参数、接口字段),可直接调用TypeScript编译API:
创建TS编译实例
用ts.createProgram加载当前工作区代码,生成Program实例,需读取项目的tsconfig.json配置以保证类型推导准确。定位表达式AST节点
解析目标文件的SourceFile,遍历AST找到模板字符串中的表达式节点。获取节点类型
调用typeChecker.getTypeAtLocation传入表达式节点,拿到完整类型对象后,通过typeChecker.typeToString转换为可读的类型字符串。
此方案需处理文件变化同步、配置加载等细节,复杂度较高,但能获取更全面的类型元数据。
复杂场景处理说明
- 箭头函数、函数调用等动态表达式:语言服务会自动推导返回值类型,直接用上述方法即可获取结果。
- 匿名对象/数组:TypeScript会进行结构类型推导,返回的类型信息包含完整的结构定义。
- JavaScript文件:只要有JSDoc类型注释,语言服务同样能正确识别类型。
内容的提问来源于stack exchange,提问作者Kyoshiro Kokujou Obscuritas

