framework 侧对应 #4118(那一个针对 objectui)。同一形状的缺口,成因不同、量级已实测。
机制:三层都不做类型检查
| 层 |
工具 |
是否类型检查 |
| build |
tsup(esbuild) — 66/77 个包 |
❌ 只转译 |
| test |
vitest run |
❌ 不做类型检查 |
| CI |
lint.yml typecheck 作业 |
⚠️ 只覆盖 4 个目标 |
CI 的 typecheck 作业实际只跑这四条:
pnpm --filter @objectstack/spec exec tsc --noEmit(lint.yml:240)
pnpm --filter './examples/*' run typecheck(:355)
pnpm --filter @objectstack/downstream-contract run typecheck(:362)
- 文档代码块抽取后
tsc --noEmit(:391)
全仓只有 2 个包(downstream-contract、service-datasource)声明了 typecheck script。其余包的 src/ 和测试都不经过任何 tsc。
这与 objectui 的成因不同,值得注意:objectui 的问题是 tsconfig 正确地排除了测试、而没有第二个 tsc 去读它们(#4118);framework 这边连 src 都没人查——因为 tsup 根本不调用 tsc。
实测范围(2026-07-31,main @ 2e836de)
方法:逐包 cd <pkg> && npx tsc --noEmit,按错误码分三层——code-tier(真缺陷)、config-tier(检查本身没配好:TS2591/TS2584 缺 types:["node"]、TS2835/TS2307 模块解析、TS2550/TS6059/TS2347/TS7016)、noise(TS7006 隐式 any 参数、TS6133 未用变量——未配置检查下的严格度噪音,两边都不计)。
77 个包:48 个干净,29 个失败;其中 18 个含 code-tier 错误。
| package |
code-tier |
config |
noise |
总 |
driver-sql |
241 |
0 |
0 |
241 |
metadata |
31 |
22 |
34 |
87 |
driver-sqlite-wasm |
27 |
0 |
0 |
27 |
driver-memory |
23 |
0 |
0 |
23 |
cloud-connection |
11 |
2 |
0 |
13 |
observability |
11 |
0 |
0 |
11 |
dogfood |
8 |
3 |
1 |
12 |
service-storage |
5 |
23 |
14 |
42 |
core |
3 |
38 |
50 |
91 |
hono / knowledge-ragflow / service-knowledge |
3 各 |
— |
— |
— |
spec-monorepo / metadata-protocol / rest / service-analytics / service-automation |
2 各 |
— |
— |
— |
service-cluster |
1 |
0 |
0 |
1 |
合计:code-tier 380 / config-tier 288 / noise 184。
注意 core(91 总数但仅 3 真错)和 service-storage(42 → 5)——不分层就会把这两个包报成重灾区,实际它们几乎全是 tsconfig 没配好。反过来 driver-sql 的 241 全部是真错。
主导模式:input-vs-output 混淆,正好压在刚收窄的 QueryAST 上
driver-sql 的 241 处集中在 TS2345(123)/TS2353(118),散布 8+ 个测试文件(非单一助手重复):
src/sql-driver-advanced.test.ts(142,50): error TS2345:
Argument of type '{}' is not assignable to parameter of type 'QueryAST'.
src/sql-driver-advanced.test.ts(151,51): error TS2345:
Argument of type '{ where: { status: string; }; }' is not assignable to parameter of type 'QueryAST'.
测试把作者态字面量传给要求 object 等 .default() 填充字段的解析后类型。这正是 #4118 指认的 TS2741/TS2739 playbook,只因这里传的是整个对象、报成了 TS2345/TS2353。
时机上值得警惕:#4196 和 #4286 刚刚收窄 QueryAST(移除 FieldNode 对象形态、joins、windowFunctions)。这类收窄本应让传旧形状的测试编译失败——而它们至今全绿,因为没有任何 tsc 读过这些文件。#4118 说的「未检查的测试会被当成证据」在这里是字面意义上的:一个 agent 引用 driver-sql 绿色测试套件来论证 QueryAST 契约,引用的是一份从未被类型系统读过的文件。
发现路径
#4198(退役 waitEventConfig.timeoutMs/.onTimeout)合并 main 后扩跑 tsc --noEmit,service-automation 报 engine.test.ts 缺 resumeAuthority(该属性由 #3822 引入)。核实后确认与那个 PR 无关、且 CI 从不检查该包,遂按 Prime Directive #10 单独立项而非扩大该 PR 范围。
建议
- 覆盖率棘轮,沿用仓库已有的最佳治理形态(声明式、带理由、只减不增),对齐 objectui 的
check-type-check-coverage.mjs:每个包要么有 typecheck script 并进 CI,要么带一条 DEBT 条目,记录实测 code-tier 数与 issue 链接。先接入 48 个已干净的包——纯管线改动、零修复成本,且立刻锁死回归。
- config-tier 模板:统一的
tsconfig.test.json(noEmit、types:["node"]、显式 lib、模块解析对齐),288 处 config-tier 应当被模板一次性消掉,而不是逐包手改。
- 按表烧 code-tier:
driver-sql(241)独占 63%,且是单一 playbook(QueryAST 作者态 vs 解析态),适合一次性集中修。
需要拍板的
TS7006(隐式 any 参数,160 处)我按 noise 计,未计入 380。若棘轮打算在测试上启用 noImplicitAny,这 160 处会变成真实工作量——建议单独决策,不要混进本 issue 的基线数字。
framework 侧对应 #4118(那一个针对 objectui)。同一形状的缺口,成因不同、量级已实测。
机制:三层都不做类型检查
tsup(esbuild) — 66/77 个包vitest runlint.ymltypecheck 作业CI 的
typecheck作业实际只跑这四条:pnpm --filter @objectstack/spec exec tsc --noEmit(lint.yml:240)pnpm --filter './examples/*' run typecheck(:355)pnpm --filter @objectstack/downstream-contract run typecheck(:362)tsc --noEmit(:391)全仓只有 2 个包(
downstream-contract、service-datasource)声明了typecheckscript。其余包的src/和测试都不经过任何tsc。这与 objectui 的成因不同,值得注意:objectui 的问题是 tsconfig 正确地排除了测试、而没有第二个
tsc去读它们(#4118);framework 这边连 src 都没人查——因为 tsup 根本不调用 tsc。实测范围(2026-07-31,
main@ 2e836de)方法:逐包
cd <pkg> && npx tsc --noEmit,按错误码分三层——code-tier(真缺陷)、config-tier(检查本身没配好:TS2591/TS2584缺types:["node"]、TS2835/TS2307模块解析、TS2550/TS6059/TS2347/TS7016)、noise(TS7006隐式 any 参数、TS6133未用变量——未配置检查下的严格度噪音,两边都不计)。77 个包:48 个干净,29 个失败;其中 18 个含 code-tier 错误。
driver-sqlmetadatadriver-sqlite-wasmdriver-memorycloud-connectionobservabilitydogfoodservice-storagecorehono/knowledge-ragflow/service-knowledgespec-monorepo/metadata-protocol/rest/service-analytics/service-automationservice-cluster合计:code-tier 380 / config-tier 288 / noise 184。
注意
core(91 总数但仅 3 真错)和service-storage(42 → 5)——不分层就会把这两个包报成重灾区,实际它们几乎全是 tsconfig 没配好。反过来driver-sql的 241 全部是真错。主导模式:input-vs-output 混淆,正好压在刚收窄的
QueryAST上driver-sql的 241 处集中在TS2345(123)/TS2353(118),散布 8+ 个测试文件(非单一助手重复):测试把作者态字面量传给要求
object等.default()填充字段的解析后类型。这正是 #4118 指认的 TS2741/TS2739 playbook,只因这里传的是整个对象、报成了 TS2345/TS2353。时机上值得警惕:#4196 和 #4286 刚刚收窄
QueryAST(移除FieldNode对象形态、joins、windowFunctions)。这类收窄本应让传旧形状的测试编译失败——而它们至今全绿,因为没有任何 tsc 读过这些文件。#4118 说的「未检查的测试会被当成证据」在这里是字面意义上的:一个 agent 引用 driver-sql 绿色测试套件来论证QueryAST契约,引用的是一份从未被类型系统读过的文件。发现路径
#4198(退役
waitEventConfig.timeoutMs/.onTimeout)合并 main 后扩跑tsc --noEmit,service-automation报engine.test.ts缺resumeAuthority(该属性由 #3822 引入)。核实后确认与那个 PR 无关、且 CI 从不检查该包,遂按 Prime Directive #10 单独立项而非扩大该 PR 范围。建议
check-type-check-coverage.mjs:每个包要么有typecheckscript 并进 CI,要么带一条 DEBT 条目,记录实测 code-tier 数与 issue 链接。先接入 48 个已干净的包——纯管线改动、零修复成本,且立刻锁死回归。tsconfig.test.json(noEmit、types:["node"]、显式lib、模块解析对齐),288 处 config-tier 应当被模板一次性消掉,而不是逐包手改。driver-sql(241)独占 63%,且是单一 playbook(QueryAST 作者态 vs 解析态),适合一次性集中修。需要拍板的
TS7006(隐式 any 参数,160 处)我按 noise 计,未计入 380。若棘轮打算在测试上启用noImplicitAny,这 160 处会变成真实工作量——建议单独决策,不要混进本 issue 的基线数字。