TypeSpec @typespec/streams 详解:用 @streamOf 装饰器与 Stream 基类描述流式协议类型
发布时间:2026/9/18 15:47:44 作者:尧图编辑部 阅读量:1,286

TypeSpec typespec/streams 详解用 streamOf 装饰器与 Stream 基类描述流式协议类型【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespectypespec/streams是 TypeSpec 官方提供的流式绑定stream bindings库用于在类型层面声明某个 Model 代表一种流协议类型其承载的数据由某个Type描述。本文将围绕 README 中介绍的streamOf装饰器展开先讲安装与基本用法再结合源码剖析装饰器的实现原理、StreamType泛型基类的工作机制以及如何在自己的库或生成器中通过getStreamOf/isStreamAPI 消费这些流信息。读完本文你将能够在 TypeSpec 项目中正确声明流式数据类型、复用Stream基类派生自定义流并了解从 AST 装饰器到程序状态Program state的完整实现链路。1. 安装按 README 的说明通过 npm 安装即可npm install typespec/streams在tspconfig.yaml中引入该库后TypeSpec 编译器会通过tspMain入口加载 lib/main.tsp。从 package.json 可以看到该包以tspMain: lib/main.tsp声明 TypeSpec 入口并在exports的typespec条件中同样指向./lib/main.tsp。注意其engines字段要求 Node.js22.0.0且typespec/compiler是peerDependencies使用时需要确保工作区中的 compiler 版本与之匹配。lib/main.tsp 的内容非常简洁它把三块东西装配成一个完整库import ../dist/src/tsp-index.js; import ./decorators.tsp; import ./types.tsp;第一行导入编译后的 JS 侧实现装饰器实现与$lib库定义第二、三行导入 TypeSpec 声明文件。也就是说这个库是一个典型的TypeSpec 声明 JS 装饰器实现组合extern dec声明在.tsp文件里真正的行为在 src/tsp-index.ts 注册。2.streamOf装饰器2.1 声明与参数streamOf声明于 lib/decorators.tspnamespace TypeSpec.Streams; /** * Specify that a model represents a stream protocol type whose data is described * by Type. * * param type The type that models the underlying data of the stream. */ extern dec streamOf(target: Model, type: unknown);其完整语义与参数如下与 README 的装饰器参考表一致声明签名TypeSpec.Streams.streamOf(type: unknown)应用目标TargetModel参数名称类型说明typeunknown描述该流底层数据underlying data的类型type之所以是unknown而不是Model从测试用例 test/decorators.test.ts 可以看出原因——标量也可以作为流数据streamOf(string) model Blob {}测试断言getStreamOf(program, Blob)返回的对象kind为Scalar、name为string。也就是说流的数据既可以是复杂的model也可以是string、bytes等任意 TypeSpec 类型。2.2 使用示例README 给出的标准示例model Message { id: string; text: string; } streamOf(Message) model Response { body body: string; }这里的语义是Response是一个流协议类型例如一个按帧/按消息推送的流端点它每次产出的数据单元的结构由Message描述。流本身可以额外携带协议字段如body body: string这类传输层描述但每个数据项是什么由streamOf的泛化参数表达。2.3 实现原理装饰器如何存储状态extern dec声明在编译后需要 JS 侧实现来落地。装饰器实现在 src/decorators.tsimport type { Model, Program, Type } from typespec/compiler; import { useStateMap } from typespec/compiler/utils; import type { StreamOfDecorator } from ../generated-defs/TypeSpec.Streams.js; import { StreamStateKeys } from ./lib.js; const [getStreamOf, setStreamOf] useStateMapModel, Type(StreamStateKeys.streamOf); export const $streamOfDecorator: StreamOfDecorator (context, target, type) { setStreamOf(context.program, target, type); }; export function isStream(program: Program, target: Model): boolean { return getStreamOf(program, target) ! undefined; } export { getStreamOf };实现链路可以拆成三步注册状态键。src/lib.ts 用createTypeSpecLibrary创建库定义并声明了一个名为streamOf的程序状态槽export const $lib createTypeSpecLibrary({ name: typespec/streams, diagnostics: {}, state: { streamOf: { description: State for the streamOf decorator. }, }, }); export const { reportDiagnostic, createDiagnostic, stateKeys: StreamStateKeys } $lib;StreamStateKeys.streamOf是这个库专属的状态键保证状态不与别的库冲突。绑定状态映射。useStateMapModel, Type(StreamStateKeys.streamOf)返回一对get/set函数把被装饰的 Model映射为其数据Type存储在Program上。这是 TypeSpec 装饰器向后续阶段如 emit 阶段传递信息的标准方式。执行装饰器。$streamOfDecorator被 src/tsp-index.ts 注册到命名空间TypeSpec.Streams下export const $decorators { TypeSpec.Streams: { streamOf: $streamOfDecorator, }, };编译器在检查到streamOf(Message)时调用该函数把type参数写入状态。对外导出的公共 API 在 src/index.tsexport { $lib } from ./lib.js; export { getStreamOf, isStream } from ./decorators.js; export { $decorators } from ./tsp-index.js;2.4 消费 APIgetStreamOf与isStream其他库例如各种 HTTP 客户端 emitter 或自定义生成器可以在 import 阶段或 emit 阶段查询流信息getStreamOf(program, model)返回被streamOf标记的数据Type若该 Model 未被装饰则返回undefined测试用例已验证这一点。isStream(program, model)便捷判断函数等价于getStreamOf(...) ! undefined。import { getStreamOf, isStream } from typespec/streams; if (isStream(context.program, model)) { const itemType getStreamOf(context.program, model); // itemType 就是 streamOf 里传入的类型 }3.StreamType泛型基类除了装饰器该库还提供了一个开箱即用的泛型模型 lib/types.tspnamespace TypeSpec.Streams; /** * Defines a model that represents a stream protocol type whose data is described * by Type. * * This can be useful when the underlying data type is not relevant, or to serve as * a base type for custom streams. * * template Type The type of the streams data. */ doc() streamOf(Type) model StreamType {}它是自装饰的Stream自身被streamOf(Type)标记而Type是它自己的模板参数。因此派生使用时只需实例化模板即可自动获得流标记。例如测试 test/decorators.test.ts 中验证的场景model Message { id: string, text: string } model CustomStream is StreamMessage {}断言结果为getStreamOf(program, CustomStream) Message。这说明通过is StreamMessage继承时装饰器效果会传递到派生模型上——测试用例名即为 is automatically set on the Stream model。StreamType的官方注释指出了两个适用场景原文直译底层数据类型不重要时直接用Streambytes之类表示这是个流具体数据后面再说作为自定义流的基类像model CustomStream is StreamMessage这样派生再补充自己的协议字段。从源码结构看streamOf作用于模板模型时Type是模板参数符号实例化派生时具体类型如Message会落到派生模型的状态上这也是测试能断言到具体 Model 的原因。4. 包的对外结构与测试整个包结构很小值得逐一确认lib/main.tspTypeSpec 入口装配 JS 实现与两个声明文件lib/decorators.tspextern dec streamOf声明lib/types.tspStreamType泛型基类src/decorators.ts装饰器实现与getStreamOf/isStreamsrc/lib.tscreateTypeSpecLibrary库定义与状态键src/tsp-index.ts$decorators/$lib注册入口src/testing/index.ts对应 package.json 中./testing导出子路径的测试辅助工具generated-defs/TypeSpec.Streams.ts由gen-extern-signature脚本tspd gen-extern-signature生成的 extern 签名类型src/decorators.ts 中StreamOfDecorator类型即来自这里。测试方面test/decorators.test.ts 覆盖了三条关键行为streamOf(string)记录标量数据、未装饰模型返回undefined、派生自StreamMessage的模型自动携带流标记。测试通过 test/test-host.ts 提供的Tester基于 src/testing/index.ts编译内联代码完成。5. 小结typespec/streams提供了一套最小但完整的流式类型原语streamOf装饰器把流的数据项类型挂到 Program 状态上StreamType泛型模型提供可继承的流基类getStreamOf/isStream则供下游库查询使用。它的当前版本为 0.86.0见 package.json要求 Node.js 22且自 0.61.0 起作为新增核心包随仓库演进参见 CHANGELOG.md。对于需要在 TypeSpec 中建模流式端点、或构建消费流语义的 emitter 的开发者这组 API 就是接入点。【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考