TypeSpec @typespec/streams 详解:用 @streamOf 装饰器与 Stream 基类描述流式协议类型
2026/9/18 15:47:23 网站建设 项目流程

TypeSpec @typespec/streams 详解:用 @streamOf 装饰器与 Stream 基类描述流式协议类型

【免费下载链接】typespec项目地址: https://gitcode.com/GitHub_Trending/ty/typespec

@typespec/streams是 TypeSpec 官方提供的流式绑定(stream bindings)库,用于在类型层面声明"某个 Model 代表一种流协议类型,其承载的数据由某个Type描述"。本文将围绕 README 中介绍的@streamOf装饰器展开:先讲安装与基本用法,再结合源码剖析装饰器的实现原理、Stream<Type>泛型基类的工作机制,以及如何在自己的库或生成器中通过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 入口,并在exportstypespec条件中同样指向./lib/main.tsp。注意其engines字段要求 Node.js>=22.0.0,且@typespec/compilerpeerDependencies,使用时需要确保工作区中的 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.tsp:

namespace 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)
  • 应用目标(Target)Model
  • 参数
名称类型说明
typeunknown描述该流底层数据(underlying data)的类型

type之所以是unknown而不是Model,从测试用例 test/decorators.test.ts 可以看出原因——标量也可以作为流数据:

@streamOf(string) model Blob {}

测试断言getStreamOf(program, Blob)返回的对象kindScalarnamestring。也就是说,流的数据既可以是复杂的model,也可以是stringbytes等任意 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.ts:

import 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] = useStateMap<Model, 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 };

实现链路可以拆成三步:

  1. 注册状态键。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是这个库专属的状态键,保证状态不与别的库冲突。

  2. 绑定状态映射useStateMap<Model, Type>(StreamStateKeys.streamOf)返回一对get/set函数,把"被装饰的 Model"映射为"其数据Type",存储在Program上。这是 TypeSpec 装饰器向后续阶段(如 emit 阶段)传递信息的标准方式。

  3. 执行装饰器$streamOfDecorator被 src/tsp-index.ts 注册到命名空间TypeSpec.Streams下:

    export const $decorators = { "TypeSpec.Streams": { streamOf: $streamOfDecorator, }, };

    编译器在检查到@streamOf(Message)时调用该函数,把type参数写入状态。

对外导出的公共 API 在 src/index.ts:

export { $lib } from "./lib.js"; export { getStreamOf, isStream } from "./decorators.js"; export { $decorators } from "./tsp-index.js";

2.4 消费 API:getStreamOfisStream

其他库(例如各种 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.Stream<Type>泛型基类

除了装饰器,该库还提供了一个开箱即用的泛型模型 lib/types.tsp:

namespace 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 stream's data. */ @doc("") @streamOf(Type) model Stream<Type> {}

它是"自装饰"的:Stream自身被@streamOf(Type)标记,而Type是它自己的模板参数。因此派生使用时,只需实例化模板即可自动获得流标记。例如测试 test/decorators.test.ts 中验证的场景:

model Message { id: string, text: string } model CustomStream is Stream<Message> {}

断言结果为getStreamOf(program, CustomStream) === Message。这说明通过is Stream<Message>继承时,装饰器效果会传递到派生模型上——测试用例名即为 "is automatically set on the Stream model"。

Stream<Type>的官方注释指出了两个适用场景(原文直译):

  • 底层数据类型不重要时:直接用Stream<bytes>之类表示"这是个流,具体数据后面再说";
  • 作为自定义流的基类:像model CustomStream is Stream<Message>这样派生,再补充自己的协议字段。

从源码结构看,@streamOf作用于模板模型时,Type是模板参数符号;实例化派生时具体类型(如Message)会落到派生模型的状态上,这也是测试能断言到具体 Model 的原因。

4. 包的对外结构与测试

整个包结构很小,值得逐一确认:

  • lib/main.tsp:TypeSpec 入口,装配 JS 实现与两个声明文件;
  • lib/decorators.tsp:extern dec streamOf声明;
  • lib/types.tsp:Stream<Type>泛型基类;
  • src/decorators.ts:装饰器实现与getStreamOf/isStream
  • src/lib.ts:createTypeSpecLibrary库定义与状态键;
  • 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、派生自Stream<Message>的模型自动携带流标记。测试通过 test/test-host.ts 提供的Tester(基于 src/testing/index.ts)编译内联代码完成。

5. 小结

@typespec/streams提供了一套最小但完整的流式类型原语:@streamOf装饰器把"流的数据项类型"挂到 Program 状态上,Stream<Type>泛型模型提供可继承的流基类,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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询