#4721 正文最后一段的顺带项:「一条更便宜的内部护栏是 lint 禁止对引擎查询选项 as any —— 是否值得单开一单,取决于树里这种擦除还有多少 」。#4721 的执行 agent 按 06:25Z 裁决要求做了这次测量,结论是值得 ,故按 Prime Directive #10 单独立项,未认领 。
实测(origin/main = 89d2a4e)
形状
非测试代码
测试代码
A. .find(…) / .findOne(…) / .count(…) / .aggregate(…) 调用里把参数 as any 掉
25
126
C. 局部变量声明成 const opts/options/query/queryOptions/findOptions: any
11
—
A 的样本(全部非测试):
packages/metadata/src/loaders/database-loader.ts:231 return this.engine.find(table, query as any);
packages/objectql/src/engine.ts:4170 await driver.find(object, …, hookContext.input.options as any)
packages/objectql/src/engine.ts:3788 ...(nestedAST.orderBy ? { orderBy: nestedAST.orderBy as any } : {}),
C 的样本:
packages/metadata-protocol/src/protocol.ts:4363 const options: any = { ...request.query };
packages/metadata-protocol/src/protocol.ts:4822 const queryOptions: any = { … };
packages/cli/src/commands/data/query.ts:75 const queryOptions: any = { … };
packages/objectql/src/hook-wrappers.ts:461 const options: any = input.options;
packages/runtime/src/domains/mcp.ts:349 const query: any = {};
packages/plugins/plugin-sharing/src/sharing-plugin.ts:735 const options: any = ctx.options;
36 处非测试擦除不是「个位数、顺手清掉」的量级,也不是可以一次性全删的量级——其中一部分(hookContext.input.options、buildDriverOptions 的返回)是真的跨了类型边界,需要的是把边界类型补上而不是删 as any。
为什么这条护栏针对的是 #4674 那一类
#4674 的两个站点都不是「缺运行时闸」漏掉的,是类型被擦掉 漏掉的:} as any) 和 const opts: any。EngineQueryOptions.orderBy 声明的就是 SortNodeSchema[],tsc 本来完全有能力在写的那一刻拒绝 direction——它没拒绝,只因为那一行把类型抹平了。#4720 恢复了那两处,#4721 关掉了外部调用方那一侧;这一单是防止同一类擦除再长出来 。
注意这三单的分工,不要混:
层
谁来管
状态
内部调用方(包内代码、协议、插件)
tsc,前提是没人擦类型
#4720 修了两处;本单防复发
外部调用方(REST / RPC 的 orderBy)
schema strict + ingress normalizer
#4721 已关
建议形状(需要决定)
范围 :只管「引擎/驱动查询选项」这一个语义位,还是所有 as any?后者必然要一张巨大的 baseline,而这条规则的价值恰恰在窄——它要说的是「查询选项的类型是有意义的,别抹」。倾向窄。
落点 :packages/lint 里的自定义规则,还是 scripts/check-*.mjs 里的一个 AST 走查(仓里已有 check-durability-log-level / check-startup-registry-verdict 两个同形状的、带 shrink-only baseline 的走查)?后者与既有惯例一致,且天然支持「已存在的 36 处进 baseline、只禁新增」。倾向后者。
测试代码是否算 :126 处在测试里。测试里 as any 常常是构造非法输入的正当 手段(一个被静默丢弃的排序键对外部调用方仍然是静默的:direction 该 400 还是继续被丢掉(#4674 第 4 项) #4721 自己的拒绝测试就要这么写)。倾向排除测试,或只在测试里禁「传给真引擎的合法查询」这一子集——但这个子集不好机械识别,所以更可能就是排除。
三条都不是实现问题,是规则边界问题,建议由维护者拍板后再动手。
关联:#4674 、#4720 、#4721 、#4363 。测量于 #4721 的实现过程。
#4721 正文最后一段的顺带项:「一条更便宜的内部护栏是 lint 禁止对引擎查询选项
as any—— 是否值得单开一单,取决于树里这种擦除还有多少」。#4721 的执行 agent 按 06:25Z 裁决要求做了这次测量,结论是值得,故按 Prime Directive #10 单独立项,未认领。实测(
origin/main=89d2a4e).find(…) / .findOne(…) / .count(…) / .aggregate(…)调用里把参数as any掉const opts/options/query/queryOptions/findOptions: anyA 的样本(全部非测试):
C 的样本:
36 处非测试擦除不是「个位数、顺手清掉」的量级,也不是可以一次性全删的量级——其中一部分(
hookContext.input.options、buildDriverOptions的返回)是真的跨了类型边界,需要的是把边界类型补上而不是删as any。为什么这条护栏针对的是 #4674 那一类
#4674 的两个站点都不是「缺运行时闸」漏掉的,是类型被擦掉漏掉的:
} as any)和const opts: any。EngineQueryOptions.orderBy声明的就是SortNodeSchema[],tsc本来完全有能力在写的那一刻拒绝direction——它没拒绝,只因为那一行把类型抹平了。#4720 恢复了那两处,#4721 关掉了外部调用方那一侧;这一单是防止同一类擦除再长出来。注意这三单的分工,不要混:
tsc,前提是没人擦类型orderBy)建议形状(需要决定)
as any?后者必然要一张巨大的 baseline,而这条规则的价值恰恰在窄——它要说的是「查询选项的类型是有意义的,别抹」。倾向窄。packages/lint里的自定义规则,还是scripts/check-*.mjs里的一个 AST 走查(仓里已有check-durability-log-level/check-startup-registry-verdict两个同形状的、带 shrink-only baseline 的走查)?后者与既有惯例一致,且天然支持「已存在的 36 处进 baseline、只禁新增」。倾向后者。as any常常是构造非法输入的正当手段(一个被静默丢弃的排序键对外部调用方仍然是静默的:direction该 400 还是继续被丢掉(#4674 第 4 项) #4721 自己的拒绝测试就要这么写)。倾向排除测试,或只在测试里禁「传给真引擎的合法查询」这一子集——但这个子集不好机械识别,所以更可能就是排除。三条都不是实现问题,是规则边界问题,建议由维护者拍板后再动手。
关联:#4674、#4720、#4721、#4363。测量于 #4721 的实现过程。