从 onSelect?.() 到 onSelect():HumanLayer Skills 移除可选回调的完整实战指南
2026/9/16 17:31:58 网站建设 项目流程

从 onSelect?.() 到 onSelect():HumanLayer Skills 移除可选回调的完整实战指南

【免费下载链接】skills项目地址: https://gitcode.com/GitHub_Trending/skills53/skills

HumanLayer Skills(skills53/skills)是 HumanLayer 开源的 Claude Code 技能合集,其中narrow-react-prop-types 技能专攻一个高频痛点:把 React 组件中形如onSelect?.(...)的可选回调收窄为必须提供的onSelect(...),让 TypeScript 类型只描述真实运行代码的状态。本文将带你一步步了解这套"移除可选回调"的方法论。

为什么onSelect?.()是类型腐化的信号?

在 React 项目中,组件的 props 往往会随着 Storybook 故事、测试 mock、演示代码不断"变宽"。最典型的征兆就是:

  • 组件接口里塞满了可选字段(onSelect?onArchive?onRename?
  • 内部用onSelect?.(item)防御式调用,生怕回调没传
  • items ?? []count ?? 0兜底那些运行时代码里其实总是有值的 prop

问题在于:每一个可选回调都意味着组件必须额外处理一条分支。菜单项明明永远渲染,点击却可能"点了没反应"——这就是可选回调制造出的"死区状态"。

HumanLayer Skills 的核心理念是:以真实业务调用方(live code paths)为唯一事实来源,让类型尽可能严格,把不可能表达的状态挡在类型系统之外。

安装 narrow-react-prop-types 技能:一键上手步骤

安装非常简单,在你的项目目录中执行:

npx skills add humanlayer/skills --skill narrow-react-prop-types

然后在 Claude Code 中直接输入/narrow-react-prop-types即可启动技能。完整安装说明见 README.md。

如果想 clone 整个仓库研究源码,可使用:

git clone https://gitcode.com/GitHub_Trending/skills53/skills

移除可选回调的 11 步工作流

该技能的完整方法论定义在 SKILL.md,共 11 个步骤,可以概括为"找 → 查 → 改 → 验"四阶段:

1️⃣ 识别"嫌疑组件"

技能列出了 5 类高危信号,其中最核心的就是可选回调调用onSelect?.(...)onArchive?.(...))和兜底状态处理items ?? [])。

💡 注意:不要仅凭一个 story 或测试就锁定目标——故事和测试只能证明"类型被放宽了",不能证明某个状态是真实存在的。

2️⃣ 穷举所有调用方并分类

搜索组件及其导出类型的所有引用,然后分成两类:

类别包含内容作用
真实代码路径应用路由、providers、hooks、生产包导出✅ 决定 props 契约的唯一依据
支撑代码Storybook、测试、fixtures、mock⚠️ 只作辅助证据

3️⃣ 逐个 prop 判定:必填、可选还是删除

这是移除可选回调的关键判据:

  • 必填:所有非测试、非 Storybook 调用方都传了这个 prop
  • 可选:至少一个真实调用方没传,且"不传"本身是有意义的运行时状态
  • 删除:没有任何真实调用方使用它(往往只是当年为 Storybook 加的)

一个容易混淆的细节:可空 ≠ 可选。如果真实代码总是传值但值可能为空,应写成必填的可空 prop(focusedItem: FocusedItem | null),而不是focusedItem?: FocusedItem | null

4️⃣ 收紧类型,并"由外向内"传播

这是本技能最精华的部分:收紧不能只停在导出的组件上

如果父组件的 props 收窄后,传给子组件的 handler 也总是有了,那么子组件内部的

onRename?.(id, name)

就应直接改为必须调用:

onRename(id, name)

技能还强调了一条铁律:如果组件总是渲染一个可交互元素(按钮、菜单项),就必须要求对应的 handler 为必填——绝不允许出现"看得见的按钮点了没反应"的死区状态。

5️⃣ 删除只为宽类型服务的兜底逻辑

可选回调移除后,一堆防御式代码会失去存在意义:

// 之前 new Set(expandedIds ?? defaultExpandedIds ?? []) // 之后 new Set(expandedIds)
// 之前 items && items.length > 0 // 之后 items.length > 0

6️⃣ 让测试和故事去适应真实代码

收窄 props 后,story 或测试报错了怎么办?反向修复:给故事补上真实合理的 handler 和状态,而不是为了让测试省事把 prop 重新改成可选。如果测试配置变得啰嗦,应创建测试辅助函数(fixture)来满足严格契约,并把它留在支撑代码一侧。

7️⃣ 类型检查验证

最后对改动的包和所有消费方跑类型检查(本技能在 monorepo 中推荐bun --bun run typecheck --filter <package>),确保共享包与消费应用都通过编译。

CI 自动化:让 AI Agent 定时执行类型收窄

这套技能不只用于手动执行,HumanLayer 还把它做成了GitHub Actions 定时 Agent 工作流的模板,位于 agent-narrow-component-props.yml。

其运行机制非常巧妙:

  • 定时/手动模式:cron 每天触发(示例为0 13 * * *),Agent 自动挑选一批高置信度的收窄改动,开 PR
  • 💬/iterate迭代模式:维护者在 Agent PR 下评论/iterate+ 反馈意见,工作流会自动拉取 PR 上下文、评论与 Agent 记忆文件,把反馈合并进提示词继续迭代,实现"人反馈 → AI 修正"的闭环
  • 📝标准化 PR 报告:每次运行按 response-template.md 输出结构化 PR 正文,包含变更表格、支撑收窄的真实调用方清单、验证结果和风险评级

其中 narrow-component-props-memory.md 是一个"Agent 记忆"文件,沉淀历次运行中维护者给出的长期约束(例如"只改apps/riptide-ui,不动apps/riptide-cloud"),让 AI 越跑越懂你的项目。

避坑清单:这些反模式千万别做

SKILL.md 结尾整理了一份"反模式"清单,建议收藏:

  • ❌ 为了让 story 可以省略 handler,把回调改成可选
  • ❌ 渲染一个调用onAction?.(...)的菜单项
  • ❌ 在真实代码是受控模式时,为 Storybook 加default*props
  • ❌ 用?? []?? 0掩盖本应由调用方保证的必填状态
  • ❌ 真实代码只用一种形态,却兼容多种 API 形状

配套的 Review Checklist 还有 10 项逐条核对项,例如"被删除的 prop 确实没有任何真实调用方使用""空值语义只保留给真实状态(如当前无焦点项)"。

写在最后

onSelect?.()onSelect(),表面上只是删掉一个?,背后却是完整的类型治理方法论:以真实代码路径为准绳、类型尽量严格、兜底逻辑随宽类型一起删除、测试反向适配契约

这套技能对新手同样友好——你不需要先成为 TypeScript 类型专家,只要跟随 SKILL.md 的 11 步工作流,配合 marketplace.json 中声明的技能市场安装方式,就能让项目的 React 组件契约一天天变得更干净。仓库里还有 improve-claude-md、show-me、design-control-loop 等技能,值得一并探索。

【免费下载链接】skills项目地址: https://gitcode.com/GitHub_Trending/skills53/skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询