Skip to content

plugin-gantt README 的 onTaskUpdate 示例在进度拖拽时写入 undefined 日期 —— changes 是 Partial,块内注释却断言 start/end 必为 Date #5058

Description

@yinlianghui

发现于 #5012 的实施(plugin-gantt README 两处虚构导入)。Filed unassigned, not claiming.

与 #5012 同文件但第三种性质:#5012 是名字不存在(TS2305),#5057 是 schema 键不存在(静默空图),本单的名字和键全是真的 —— 错的是可选性,以及块内注释对它的断言。名集合核对与「把块喂 tsc」两道检查都看不见这一类:在一个宽松的 save() 签名下这个块是编译通过的。与 #5016 → #5048 的分工同形。

测于 origin/main @ f331f5a8bdb728039519ace070f6433f597e0dd4。

现象

packages/plugin-gantt/README.md,"Drag-and-drop rescheduling" 一节(#5012 PR 后 :66-73):

< GanttView
  tasks={tasks}
  onTaskUpdate={(task, { start, end }) => {
    // start/end are JS Date objects already snapped to whole days,
    // and the resize-left/right cases clamp so end - start >= 1 day.
    save(task.id, { start, end });
  }}
/>

那条注释对 start / end 的断言两层都不成立。

类型层。 GanttViewProps.onTaskUpdate(packages/plugin-gantt/src/GanttView.tsx:345)的第二参是

changes: Partial< Pick< GanttTask, 'title' | 'start' | 'end' | 'progress' > >

所以解构出来的 start / end 是 Date | undefined,不是 Date。读者若按注释把它们喂给一个要求必填 Date 的函数,拿到的是 TS2322 ×2(实测:把 save 声明成 (id, { start: Date; end: Date }) 后该块编译报错两处,正是 start 与 end)。

运行时层,这一层更要紧。 同一个 onTaskUpdate 不只服务拖拽。GanttView.tsx:1335,进度拖拽提交:

if (task && onTaskUpdate) commitTaskUpdates([{ task, changes: { progress: cur.value } }]);

changes 里没有 start / end。commitTaskUpdates 原样转发(:1089 onTaskUpdate(task, changes)),于是一个只接了 onTaskUpdate 的宿主,照抄这个块之后每次用户拖进度圆点都会调用

save(task.id, { start: undefined, end: undefined })

也就是把两个日期字段写成空。README 前文 :62-64 正是教「嵌 < GanttView > 时传 onTaskUpdate 来开启拖拽」,而进度拖拽并不需要额外开关,所以这条路径是默认可达的,不是边角配置。

(其余提交路径的形状,供改写时参考:拖拽/缩放走 computeDragChanges 带 start+end;行内编辑 :3646 带 title+start+end+progress 四个;撤销/重做 :1185 回放的是记录下来的键集;自动排期 :2911 带日期。只有进度拖拽是纯 { progress }。)

修法(需裁定的一点)

块内应当按 changes 的真实形状取值,例如只在两者都在时才写回:

onTaskUpdate={(task, { start, end }) => {
  if (!start || !end) return;   // progress / title 提交不带日期
  save(task.id, { start, end });
}}

要裁的是注释怎么改:是像上面那样只演示日期分支并说明其他提交形状,还是把这个块扩成一个按键分派的完整示例(日期 / 进度 / 标题各一路)。前者最小,后者更贴合「这个回调是所有编辑路径的单一出口」这个事实 —— 交分诊定。

顺带:同一节的散文说「On release ObjectGantt issues an optimistic local patch and a dataSource.update(...)」,这半边是准确的,不在本单范围。

复核方式

grep -n "onTaskUpdate?: (task: GanttTask" packages/plugin-gantt/src/GanttView.tsx      # :345 第二参是 Partial 包 Pick
grep -n "changes: { progress: cur.value }" packages/plugin-gantt/src/GanttView.tsx     # :1335 纯 progress 提交
grep -n "onTaskUpdate(task, changes)" packages/plugin-gantt/src/GanttView.tsx          # :1089 原样转发
sed -n '62,74p' packages/plugin-gantt/README.md

分级

交 PM 分诊。倾向具体缺陷:不是文档口味问题,照抄这个块会在用户拖进度时把记录的开始/结束日期写空 —— 数据侧的后果,比 TS2322 那半边严重。体量很小(一个代码块 + 注释)。

关联:#5012(同文件的导入名半边,在途)、#5057(同文件的 schema 键半边)、#5048(plugin-report「名字真、调用错」的同构先例)、#5043(缺门禁的根因单;本条也是它「更强的一档」都抓不住的形状)。

Activity

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

    @yinlianghui
    CollaboratorAuthor

    认领(与 #5057 合单)(objectui 分片 PM 直辖,批次 22 补席)—— session session_01GTRjn8xBqp75dk7kFupVRt,分支 claude/issue-5057-gantt-schema-keys,一个 PR 同时 Fixes #5057 + Fixes #5058。

    卡面裁定点 PM 代裁(留否决窗):采最小形 —— 示例只演示日期分支(if (!start || !end) return; 守卫)并以一句注释说明其余提交形状(progress/title 提交不带日期),⛔ 不扩成按键分派的完整示例。理由:README 示例的职责是教安全的最小正确用法,消除「拖进度写空日期」的数据侧危害即达标;「回调是所有编辑路径单一出口」的完整分派叙述属 API 参考层,现有块内注释按 Partial 真形改写即可。维护者可否决。


    Generated by Claude Code

  3. yinlianghui commented on Aug 17, 2026

    @yinlianghui
    CollaboratorAuthor

    已实施 → PR #5072(draft),session session_01GTRjn8xBqp75dk7kFupVRt,基线 origin/main @ 82a94170c4058d451ce3ac179d99296d90554479。

    前提完全成立,三个读取点逐条复核无误:GanttView.tsx:345 第二参是 Partial 包 Pick;:1335 进度拖拽提交纯 { progress: cur.value };:1091 原样转发 onTaskUpdate(task, changes)。

    按 PM 代裁(评论 5320691422)采最小形:if (!start || !end) return; 守卫 + 一句注释说明其余提交形状,未扩成按键分派示例。实施中我一度多写了一句「onTaskUpdate 是所有编辑路径的单一出口」的散文,发现越界后随即删除 —— 该叙述被 PM 明确划归 API 参考层。同节散文「optimistic local patch + dataSource.update」半边按卡面划定未动。

    反向验证(方向动手前预判,结果一致未反转):把 save 声明成必填 Date 版,探针对构建产物 dist/index.d.ts 编译、strict ——

    • 改前(main 上的块逐字回填):TS2322: Type 'Date | undefined' is not assignable to type 'Date' ×2,分别在 start 与 end 列位,exit=2;
    • 改后(PR 落地的块逐字回填,同一 save 签名):exit=0。

    合单的 #5057 未随本 PR 改动 —— 其逐键零命中表有一行(viewMode)被实测推翻,已在 #5057 留报告并另立 #5074,交 PM 重新分诊。


    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

Labels

bugSomething isn't workingdocumentationImprovements or additions to documentationpm:queue

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions