参数绑定
参数装饰器按参数索引绑定,不根据 TypeScript 声明类型绑定。显式名称能保留到压缩构建后,是路径/查询/头字段的可靠选择。
绑定矩阵
parameter(type: ParameterType, name = '') 返回传统方法参数装饰器。ParameterMetadata 保存 type、可选 name、index 和可选 explicit(名称是否由装饰器给出而非推断);PARAMETER_METADATA_KEY 是目标/属性上的元数据 Symbol。
参数按形状绑定。只有普通对象(对象字面量或 Object.create(null))会展开为键;其他值,包括数组、Date 和类实例,都绑定到参数名(显式名称,否则推断名称,否则 param${index}),由 fetcher 序列化。
| 工厂 / ParameterType | 其他参数(标量、数组、Date、实例) | 普通对象参数 | Nullish 行为 |
|---|---|---|---|
path(name = '') / PATH | 绑定到参数名 | 展开条目,忽略显式 name | 跳过 null/undefined 参数或条目 |
query(name = '') / QUERY | 绑定到参数名;数组重复该键(ids=1&ids=2),Date 以 ISO 8601 发送 | 展开条目 | 跳过 null/undefined 参数或条目 |
header(name = '') / HEADER | 设置以参数名命名的请求头;数组以 , 连接,Date 以 ISO 8601 发送 | 每个条目设置一个请求头,大小写不敏感 | 整个参数 null/undefined 时跳过,条目为 null/undefined 时删除该头 |
body() / BODY | 整个请求正文 | 整个请求正文 | 按原值赋值,再参与 request 合并 |
request() / REQUEST | 应为 ParameterRequest | 最后合并到已解析请求 | 假值变为空请求 |
attribute(name = '') / ATTRIBUTE | 存入参数名下 | 显式名称:整体存入该名下;未命名:合并条目 | 跳过 undefined;null 与其他值一样存入 |
显式命名的 @attribute('user') 无论值是什么都存入 user 名下。未命名的 @attribute() 逐条合并 Map 或普通对象,其他值存入推断名称下。
参数从左到右处理,后绑定优先。body/request 每种只选择一个值,因此最后一个此类参数获胜,并非合并多个 request 参数。普通未注解参数被忽略。
ParameterRequest<BODY> 扩展 FetchRequestInit<BODY> 与 PathCapable。其 path 改变端点路径,替换值应放在 urlParams.path。它可覆盖 method、headers、body、timeout、signal 和原生 Fetch 字段,遵循 mergeRequest 规则(含 nullish 回退)。空字符串参数路径不会覆盖非空端点路径。
替换后的方法只接收实际参数列表,原占位方法的默认参数表达式不会执行。请显式传入默认值,或在 urlParams 元数据中配置。
名称与反射
getParameterNames(func): string[] 解析 Function.toString(),以函数为键在 WeakMap 缓存;解析失败返回空数组,非函数在解析前抛 TypeError。实现按顶层逗号分割(默认值、字符串和注释中的逗号与括号不会分割),并去掉类型与默认值。解构参数得到 '',使后续名称保持原索引;剩余参数得到去掉 ... 的名称。压缩后的名称不是稳定契约。
getParameterName(target, propertyKey, index, providedName?) 优先返回真值显式名称,再尝试推断,否则 undefined(解构参数没有推断名称)。没有解析名称的绑定值回退到 param${index}。未命名 @path() 的推断名称若不匹配路径模板(按 fetcher 的 urlTemplateStyle 读取)的任何占位符,每个端点只输出一次警告:这正是压缩器改名参数的表现。纯对象参数(展开为键)不会警告。没有参数的占位符不报告,因为拦截器可能补上(cosec 补 {tenantId}/{ownerId});若无人补上,解析 URL 会以 Missing required path parameter 失败。
取消与继承元数据
AbortSignal 或 AbortController 参数在装饰器元数据之前识别,即使没有装饰器也有效。多个同类参数最后一个优先;request 参数还能覆盖。signal 或 controller 与 Fetcher timeout 同时生效,先触发者生效,详见取消。
继承参数元数据采用写时复制,装饰重写方法不会修改父类 Map。未加自身端点装饰器的重写方法保留自己的实现,@api 不会用父类的请求替换它。类绑定遍历继承的字符串与 Symbol 命名方法,静态方法不在该遍历中。继承/重写端点也应显式填写参数名称。
完整示例
import {
api,
post,
path,
query,
body,
request,
autoGeneratedError,
type ParameterRequest,
} from '@ahoo-wang/fetcher-decorator';
type User = { id: string; name: string };
@api('/users')
class Users {
@post('/{id}')
update(
@path('id') id: string,
@query('notify') notify: boolean,
@body() value: { name: string },
@request() options?: ParameterRequest,
signal?: AbortSignal,
): Promise<User> {
throw autoGeneratedError(id, notify, value, options, signal);
}
}
const users = new Users();
async function updateUser() {
const controller = new AbortController();
return users.update(
'1',
true,
{ name: 'Ada' },
{ headers: { 'X-Trace-Id': 'demo' } },
controller.signal,
);
}
void updateUser;公开符号与源码
| 符号 | 实现 |
|---|---|
ParameterType | parameterDecorator.ts:19 |
ParameterMetadata | parameterDecorator.ts:136 |
PARAMETER_METADATA_KEY | parameterDecorator.ts:168 |
parameter | parameterDecorator.ts:206 |
path | parameterDecorator.ts:273 |
query | parameterDecorator.ts:305 |
header | parameterDecorator.ts:337 |
body | parameterDecorator.ts:355 |
ParameterRequest | parameterDecorator.ts:367 |
request | parameterDecorator.ts:387 |
attribute | parameterDecorator.ts:423 |
getParameterNames | reflection.ts:51 |
getParameterName | reflection.ts:97 |
