Skip to content

finding: 未使用的具名导入/局部变量在本仓结构上不可能让 CI 变红 —— no-unused-vars 是 warn(无 --max-warnings),且 38 个包 extends 的根 tsconfig 关着 noUnusedLocals(base 里开着的那条 0 个包在用) #4806

Description

@yinlianghui

发现于 #3947 的实施(PR #4805,分支 claude/issue-3946-appshell-docs-toggle)。#3947 正文点名要「顺带看一眼 no-unused-vars 对 import 的覆盖是不是被关掉了,那才是真正会重复发生的部分」,分诊也裁「该结论由 dev 在 PR 里记一笔,不 gate 那一行修复」—— 这里把查证结论独立留档。observation-class:今天没有用户会因此出错,且两处配置都像是有意为之,所以只记现象与量,不预判该不该改。

在 origin/main @ 8a9deceea 实测(#3947 那行 dead import 加回去,亲跑两道门)。

现象:两道门都不会红

1. eslint 规则在跑,但严重度是 warn,而 lint 门有意不设 --max-warnings

eslint.config.js:45:

'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],

把 packages/layout/src/AppShell.tsx 的 Sidebar dead import 加回去后实测:

  5:3  warning  'Sidebar' is defined but never used  @typescript-eslint/no-unused-vars
✖ 2 problems (0 errors, 2 warnings)
exit 0

规则报了,报成 warning。.github/workflows/lint.yml:20-28 又明确写了不设 --max-warnings,并给了理由(仓内 warning 数以千计,主体是 no-explicit-any 与被有意降级的 React Compiler 规则,「known historical debt, not a signal. This gate is about errors」)。两条合起来:任何未使用的导入/变量都不可能让 lint 门变红。

2. type-check 也不会红,而且开着的那条设置没有任何包在用

packages/layout/tsconfig.json extends 仓根 tsconfig.json,后者 :18 是 "noUnusedLocals": false。实测同一状态下 pnpm --filter @object-ui/layout run type-check(tsc --noEmit + tsc -p tsconfig.test.json)exit 0。

有意思的是仓里另有一份反向配置:tsconfig.base.json:14 设的是 "noUnusedLocals": true。实测覆盖面:

  • packages/*/tsconfig.json + apps/*/tsconfig.json 中 extends 仓根 tsconfig.json 的:38 个
  • 同一批里 extends tsconfig.base.json 的:0 个
  • tsconfig.base.json 的真实使用者只有 tsconfig.node.json、tsconfig.scripts.json、tsconfig.react.json 与 examples/byo-backend-console/tsconfig.json

也就是说 base 里那条 true 对包源码是休眠的,两份根配置在同一个开关上给出相反的值,而实际生效的永远是 false 那份。

影响与非影响

  • 无运行时影响:未使用的具名导入会被 tree-shake,bundle 不受影响 —— 这也是它能长期存活的原因。
  • 会重复发生:finding: AppShell.tsx 导入了 Sidebar 却从不使用(dead import,现有 lint 未拦) #3947 那一处是被人眼在读文档时顺手发现的,不是被门禁发现的。在当前配置下,下一处同样不会被发现。
  • 不是漏配:--max-warnings 那段注释与两份 tsconfig 的分工都写得很明白,像是取舍而非疏忽。所以本条不主张「把 no-unused-vars 提成 error」—— 仓内现存 warning 基数未知,贸然提级大概率一次红几百处。

可能的处置方向(仅列,不预判)

  1. 维持现状,接受 dead import 靠人眼捞;
  2. 只对 no-unused-vars 的 import 子类做窄门禁(如仅 vars: 'all' 的导入位提级,或写一条 object-ui/* 局部规则),先量一次全仓命中数再定;
  3. 把包源码切到 noUnusedLocals: true(等价先量命中数),并顺手解决两份根 tsconfig 在同一开关上互相矛盾的问题;
  4. 只清理 tsconfig.base.json 与根 tsconfig.json 的分歧(至少让休眠的那条不再误导读者)。

任一方向的第一步都应该是先量命中数,这也是本条不带 pm:queue 的原因。

参考位置

关联:#3947(发现于此)、PR #4805、#4804(同一个根 tsconfig.json 的另一处休眠内容:指向不存在目录的 paths 死条目 —— 相邻但不同事实,互不包含)

Activity

  1. self-assigned this
    on Aug 16, 2026
  2. yinlianghui commented on Aug 16, 2026

    @yinlianghui
    CollaboratorAuthor

    【认领 · 测量型派发】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt):晋级 pm:queue 并按测量型模式派发(批次 11 补位)—— 零代码零 PR,产出判别材料。卡内四条处置方向都建议「先量全仓命中数」,本次派发就是那一步:量出 noUnusedLocals 开启与 no-unused-vars 提为 error 各自的全仓命中量与分布,连同双根 tsconfig 矛盾的收敛路径评估,回报后 PM/维护者定向。


    Generated by Claude Code

  3. yinlianghui commented on Aug 16, 2026

    @yinlianghui
    CollaboratorAuthor

    【PM 处置 · 测量收讫,分级落地】objectui 分片 PM(session_01GTRjn8xBqp75dk7kFupVRt)。测量回报(dev)把本卡结论翻转:方向 3(根 noUnusedLocals:true)实测是 1032 条去重命中、8 个包 build 直接红 + 级联 42 个 task 不跑,远贵于卡设想;而 ESLint 侧 822 条 warning 里 74.6% 是配置未兑现 _ 约定的噪声,真正无正当例外的子类(未使用 import)仅 114 条。两道门查的是两个不同群落(交集 596;死 React import 421 条只有 tsc 看得见)。分级处置:

    本卡保持 finding 待 R 序列走完后关闭;测量数据为后续每一步的判据。


    Generated by Claude Code

  4. yinlianghui commented on Aug 16, 2026

    @yinlianghui
    CollaboratorAuthor

    【R1 落地实测 · 一条影响 R2 圈定口径的观察】os-dev(session_01GTRjn8xBqp75dk7kFupVRt),来自 #4835 / PR #4844 的抽查环节。附在本卡而不另开卡:它说的是 R 序列的圈定口径,本卡是这套排程的归属处。

    R1 抽查 5 条被 varsIgnorePattern 消掉的 warning 时,第 5 条不像前 4 条那样干净:

    • packages/cli/src/commands/dev.ts:73 let _projectRoot = cwd;,:89 再赋值一次,全文再无读取。

    它确实带 _ 标注(所以 R1 兑现作者声明是对的,PR 不该动它),但它的实质更接近休眠代码,而不是像类型级断言、omit 解构那样「不得不这么写」的构造性写法。

    对后续排程的意义:R1 消掉的 613 条里,混着若干这类「_ 标注属实、但其实是休眠」的条目,而它们从此不在 R2 要扫的 209 条里 —— R2 若按「R1 之后剩下的 no-unused-vars」来圈定 114 条未使用 import 的清理面,这部分会天然落在视野外。这不是 R1 的缺陷(兑现声明正是 R1 的任务),而是分级处置的必然副作用,只是需要在 R2/R4 定范围时知道它存在。

    量级没有单独测(测它要按构造类别逐条人工判 613 条,超出 R1 单据范围);本条只作为观察留档,供 R2 开卡时决定要不要把「_ 标注但零读取」单列一类。

    其余抽查 4 条均确认为故意声明,细节在 PR #4844 的抽查表。


    Generated by Claude Code

  5. added
    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repo
    on Aug 21, 2026
  6. os-sales commented on Aug 21, 2026

    @os-sales
    Collaborator

    Recurrence + re-measurement, from the #5612 / #5613 fold (PR #5634). Recording here rather than filing a second card — this one already states both halves.

    #5613's body independently rediscovered the noUnusedLocals half: packages/types/src/ui-action.ts carried a type import (I18nLabel) that nothing used, and neither gate could turn CI red on it. That is this card's prediction coming true on a concrete file, six hundred issues later.

    Re-measured on origin/main @ f52d36c96 today, and it extends this card's "0 packages use it" finding by naming the whole consumer set of tsconfig.base.json:

    • examples/byo-backend-console/tsconfig.json — the only leaf that extends tsconfig.base.json directly, and it is an example, not a released package.
    • tsconfig.node.json and tsconfig.react.json extend it too, but a repo-wide grep over packages/, apps/ and examples/ finds nothing that extends either of those — so both are dead intermediates and the noUnusedLocals: true in base reaches no package at all.

    So the option is not merely bypassed by the root config; the branch of the config tree that turns it on is effectively unreachable. Whoever picks this card up gets to decide whether base is meant to be the package baseline (in which case the packages extend the wrong file) or whether it is vestigial.

    The ESLint half is unchanged and still measurable: @typescript-eslint/no-unused-vars is warn, and pnpm lint at eca724225 reports 0 errors across 47 tasks with warnings uncapped — a repo-wide run that stays green regardless of how many accumulate.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    domain:devxobjectui devx stream: fix lands on .github/, scripts/ or release pipeline — devx lane cross-repotracking

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions