TypeSpec 类型信息提供器($provideTypeInfo)实战:为 IDE 悬停与工具链贡献领域专属类型信息

发布时间:2026/9/19 6:30:27
TypeSpec 类型信息提供器($provideTypeInfo)实战:为 IDE 悬停与工具链贡献领域专属类型信息
TypeSpec 类型信息提供器$provideTypeInfo实战为 IDE 悬停与工具链贡献领域专属类型信息【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec导读TypeSpec 语言本身只描述类型结构而领域语义如某个op的 HTTP 路由、响应状态码通常由typespec/http等库在编译期另行推导。为了让这些语言之外的领域信息也能被 IDE 悬停提示和 AI Agent 等工具查询到TypeSpec 编译器提供了一套实验性的type-info-provider特性库可以通过导出$provideTypeInfo函数按需为任意类型贡献一段 Markdown 内容。阅读完本文你将掌握如何在自己的 TypeSpec 库中注册该提供器、如何正确发布包含tspconfig.yaml的包以及如何通过program.getTypeInfo在工具侧统一查询所有库贡献的信息。本文对应仓库文档为 providing-type-info.md源码证据分布在 compiler 核心类型定义、library.ts、program.ts 以及 typespec/http 的真实实现 中。为什么需要类型信息提供器TypeSpec 的核心语言只提供类型、操作、命名空间等语法设施。当用户编写一个op时编译器并不知道它对应的 HTTP 方法、路径模板或响应状态码——这些语义由typespec/http这样的库通过装饰器与编译期推导得出属于领域专属信息天然不包含在核心语言的类型体系里。过去这些信息只存在于编译产物如 OpenAPI 文档中开发者在 IDE 里悬停一个操作时无法直接看到。type-info-provider特性正是为了解决这个缺口它允许库为类型贡献额外的、领域化的信息这些信息会显示在 IDE 悬停hover提示中附加在类型签名与文档注释之后通过program.getTypeInfo被工具包括 AI Agent编程式查询。在 typespec/http 的官方实现 中一个 Operation 会贡献出HTTP Route方法 URI 模板以及Responses状态码列表这就是额外信息的典型例子。实验性特性与库侧开启方式该特性目前处于实验阶段且开启opt-in粒度是声明提供器的库自身库作者必须在自己库的tspconfig.yaml中启用type-info-provider编译器特性而使用该库的消费者无需任何额外配置即可看到贡献的信息。在库的tspconfig.yaml中加入kind: project features: - type-info-provider该特性名称与说明在 compiler 的 features.ts 中登记官方描述为启用实验性的$provideTypeInfo提供器允许库为类型贡献额外信息供 IDE 悬停与工具通过program.getTypeInfo查询使用。同文件还列出了其他实验特性如function-declarations、auto-decorators说明features是编译器统一的实验开关机制。发布时必须带上 tspconfig.yaml由于 opt-in 标记是从已发布的包中读取的务必确认tspconfig.yaml真的被打包发布否则库从 registry 安装后提供器会被静默忽略。需要在库的package.json的files字段中显式包含它{ files: [lib/**/*.tsp, tspconfig.yaml, dist/**] }如果tspconfig.yaml缺失或未列入files编译不会报错但$provideTypeInfo不会被注册——这种静默失效是发布阶段最容易踩的坑。核心 API$provideTypeInfo与defineTypeInfoProvider库从主入口文件导出一个$provideTypeInfo函数即可注册提供器。推荐使用defineTypeInfoProvider辅助函数来获得完整的类型标注它本身只是一个恒等函数仅提供类型帮助见 library.ts。提供器接收一个TypeInfoContext包含program当前的Program实例用于读取编译期状态target当前被查询的类型Type。返回一个TypeInfo对象目前只有一个content字段要展示的 Markdown 内容当该提供器对该类型没有可贡献的内容时返回undefined。官方文档示例import { defineTypeInfoProvider } from typespec/compiler; import { getHttpOperation } from ./operations.js; export const $provideTypeInfo defineTypeInfoProvider(({ program, target }) { if (target.kind ! Operation) { return undefined; } const [operation] getHttpOperation(program, target); if (!operation) { return undefined; } return { content: \HTTP Route\: \${operation.verb.toUpperCase()} ${operation.uriTemplate}\, }; });对应的类型定义见 types.tsTypeInfo接口只有只读的content: string字段TypeInfoContext由program与target组成TypeInfoProvider是(context) TypeInfo | undefined的函数类型。官方实现参考typespec/httptypespec/http是仓库内最直接的实现范例packages/http/src/type-info.ts其逻辑比文档示例更进一步export const $provideTypeInfo defineTypeInfoProvider(({ program, target }) { if (target.kind ! Operation) { return undefined; } const [operation] getHttpOperation(program, target); if (!operation) { return undefined; } const lines [\HTTP Route\: \${operation.verb.toUpperCase()} ${operation.uriTemplate}\]; const statusCodes operation.responses.map((response) formatStatusCode(response.statusCodes)); if (statusCodes.length 0) { lines.push(\Responses\: ${statusCodes.map((code) \${code}\).join(, )}); } return { content: lines.join(\n\n) }; });值得注意的实现细节它通过target.kind ! Operation快速短路对非 Operation 类型直接返回undefined使用getHttpOperation(program, target)解析出操作的路由与响应若解析失败同样返回undefinedformatStatusCode统一了三种状态码形态数字204、通配符*、区间200-299输出为start-end形式多行内容用\n\n连接与program.getTypeInfo中的合并策略一致。IDE 中的展示方式在 IDE 中提供器贡献的内容被追加在类型签名与文档注释之后并用一条水平分隔线与类型自身的 doc 注释区分开op read(id: string): void Reads a pet. --- HTTP Route: GET /pets/{id} Responses: 204即第一段是类型签名与文档---之后是来自$provideTypeInfo的领域信息。这种布局让悬停提示既保留原有文档又能清晰呈现库补充的语义如 HTTP 路由。重要约束懒执行、只读、无顺序竞争文档特别强调了$provideTypeInfo与$onValidate该文档位于 website/src/content/docs/docs/extending-typespec/diagnostics.md生命周期钩子的关键区别编译期绝不执行。提供器是懒加载、按需调用的例如语言服务器计算悬停文档时、工具查询时不会拖慢正常编译流程。不得修改类型图。提供器只能读取 program 并回答关于它的问题任何变更类型图的行为都是禁止的。正是因为不改变类型图库之间不存在执行顺序或竞态问题——每个库贡献的content只是被简单地拼接concatenate在一起。编程式查询program.getTypeInfo工具侧可以通过program.getTypeInfo(target)查询某个类型上所有已注册提供器的贡献结果返回值是合并后的单个TypeInfo当没有任何提供器贡献内容时返回undefinedconst info program.getTypeInfo(type); // { content: HTTP Route: GET /pets/{id}\n\nResponses: 204 }源码级的合并与容错机制getTypeInfo的实现在 program.ts其中包含几个文档未展开的关键细节getTypeInfo(target) { if (typeInfoProviders.length 0) { return undefined; } const contents: string[] []; const context: TypeInfoContext { program, target }; for (const provider of typeInfoProviders) { let result: TypeInfo | undefined; try { result provider.callback(context); } catch (error: any) { // 懒执行发生在编译结束后很久崩溃的提供器不得污染 program 的诊断 if (options.designTimeBuild) { trace( info-provider.crash, Library ${provider.metadata.name ?? unnamed} $provideTypeInfo crashed: ${error.stack}, ); continue; } else { throw new ExternalError({ kind: info, metadata: provider.metadata, error }); } } if (result) { contents.push(result.content); } } return contents.length 0 ? { content: contents.join(\n\n) } : undefined; }从该实现可以确认三点行为异常隔离在设计时构建designTimeBuild即语言服务器场景中某个库的提供器抛异常会被 trace 到info-provider.crash并跳过不会让悬停或补全功能整体挂掉非设计时构建中则抛出ExternalError。这正是懒执行、晚于编译这一约束在工程上的体现——此时再往program里塞诊断已经没有意义。贡献合并多个库的content以\n\n连接与typespec/http内部多行拼接方式一致最终合并为单个TypeInfo。空结果语义无提供器或无内容时返回undefined调用方需要处理这一情况。完整落地清单要在自己的 TypeSpec 库中启用并提供类型信息按以下步骤操作在库的tspconfig.yaml中开启特性kind: project features: - type-info-provider在库主入口导出$provideTypeInfo用defineTypeInfoProvider包裹对无关类型返回undefined必要时借助getHttpOperation等编译期 API 解析领域语义参考 typespec/http 实现。确认发布配置在package.json的files中包含tspconfig.yaml避免从 registry 安装后被静默忽略。验证在 IDE 中悬停目标类型查看追加内容或通过program.getTypeInfo(type)编程式断言输出可参考 compiler 的 types.ts 中TypeInfo/TypeInfoContext的接口形态编写类型安全的测试。小结type-info-provider为 TypeSpec 的库生态打开了一条领域语义 → IDE/工具的低成本通道库作者只需导出一个纯函数式、只读的提供器即可把 HTTP 路由、状态码等编译期推导结果注入悬停提示供开发者与 AI Agent 查询。其设计约束懒执行、不可变、无竞争使它天然适合叠加多个库的贡献而program.getTypeInfo的异常隔离与内容合并机制则保证了即便某个库的实现存在缺陷也不会拖垮整个设计时体验。【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考