mcpField
E
experience-lwc-typescript-migrate

experience-lwc-typescript-migrate

github.com/forcedotcom开发1.1k2.3k 次安装1 天前更新
代码仓库

关于 experience-lwc-typescript-migrate

用于将 Lightning Web Components(LWC)从 JavaScript 迁移到 TypeScript 的技能,面向维护 Salesforce 组件的开发者。它强调保留 Git 历史、为公开的 @api 接口生成仅含公开成员的 .d.ts,并通过编译和测试验证。

主要功能

  1. 01将组件和辅助文件重命名为 .ts
  2. 02为公开及内部成员添加类型
  3. 03生成仅包含 @api 成员的 .d.ts
  4. 04检查事件与异步方法类型
  5. 05运行 TypeScript 编译和 Jest 测试

适用场景

  1. 01将单个 LWC 从 JavaScript 迁移到 TypeScript
  2. 02为现有 LWC 创建公开类型声明
  3. 03补全已重命名组件的 TypeScript 类型

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/forcedotcom/sf-skills --skill experience-lwc-typescript-migrate

安装后在 Claude Code 中输入/experience-lwc-typescript-migrate确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

assets/
dts-template.ts
type-patterns.ts
scripts/
find-consumers.sh

详细介绍

将 LWC 转换为 TypeScript

将 Lightning Web Component 包从 JavaScript 转换为 TypeScript。交付内容为一份类型完整的 .ts 实现,以及一个仅公开 @api 成员的 .d.ts 文件(其他 LWC 会使用这些公共接口)。

何时使用此技能

  • 用户想将单个组件或一个组件文件夹从 .js 迁移到 .ts。
  • 用户需要为现有 LWC 编写 .d.ts,以便其他组件(或外部 TypeScript 宿主)安全地导入它。
  • 用户正在为已重命名为 .ts、但尚未正确添加类型的 LWC 添加类型注解。
  • 用户想将 JSDoc 风格的类型提示升级为真正的 TypeScript 类型。

前置条件

  • 组件目前使用 JavaScript 时可以正确构建和运行。
  • 系统中有可用的 git(重命名必须通过 git mv 保留历史记录)。
  • 构建流程中已接入 TypeScript 编译器(SFDX TS 流程或独立的 tsc 步骤均可)。

工作流程

步骤 1 — 阅读组件

打开组件包中的每个文件:

componentName/
├── componentName.js
├── componentName.html
├── componentName.css
└── (possibly) __tests__/, __utam__/, existing .d.ts

了解以下内容:

  • 哪些类继承了 LightningElement?类名是什么?
  • 哪些字段和方法带有 @api 装饰器?
  • 哪些属性或方法已有 JSDoc(可将其作为类型提示的起点,但要根据实际用法进行验证——JSDoc 可能不准确)。
  • 根据代码内部的调用方式,可以推断出哪些参数和返回类型?
步骤 2 — 使用 git mv 将 .js 重命名为 .ts
git mv componentName/componentName.js componentName/componentName.ts

对组件包中的任何辅助 .js 文件重复此操作(除非它们已经是 .ts)。绝不要直接使用 mv——这会丢失 TypeScript 审核人员依赖的历史关联。

步骤 3 — 在 .ts 中添加类型注解

按以下优先级顺序添加类型;一旦公共契约完整,就可以停止:

  1. 先处理 @api 属性和方法。 如果缺少 JSDoc,就生成 JSDoc,然后将 JSDoc 类型转换为 TS 语法(string、number、boolean、Promise<T>)。在采信每条 JSDoc 描述之前,都要先根据代码进行验证。
  2. 复杂结构应定义为 interface 或 type 别名,不要在各处重复写内联结构。
  3. 仅当值确实允许为 undefined 时,才使用 ? 表示可选成员。 不要出于防范而到处添加 ?。
  4. 私有或内部状态——仍需标注类型,但不要导出这些类型。对于绝不能由使用方访问的成员,使用 private。
  5. 事件处理程序——优先使用精确的 DOM 事件类型:
    • 对 onclick(以及其他类似点击的处理程序)使用 MouseEvent。click 会作为 MouseEvent 分派——包括由键盘触发的点击——因此将其标注为 PointerEvent,会让处理程序依赖在这些情况下未定义的指针专属字段(pointerType、pressure 等)。
    • 对 onpointerdown / onpointerup / onpointermove 以及其他确实需要指针专属字段的 pointer* 处理程序,使用 PointerEvent。
    • 对 LWC 自定义事件,使用 CustomEvent<{ detail: ... }>。
    • Event 是最后的选择;使用时请说明原因。
  6. 异步方法始终返回 Promise<T>,不要直接返回 T。
  7. 避免使用 any。 如果确实无法为某项内容标注类型,请使用 unknown,并通过类型守卫进行缩窄。
参考模式

加载 [[assets/type-patterns.ts|assets/type-patterns.ts]],以查看属性类型、方法类型和事件处理程序类型的内联示例。

步骤 4 — 生成 .d.ts

在 .ts 文件旁创建 componentName.d.ts。文件必须:

  • 仅包含 @api 成员——不能包含私有状态、内部方法,除非生命周期钩子本身带有 @api,否则也不能包含。
  • 将 @api JSDoc 原样保留(包括 @type、@required、@default、@param、@returns 标签),并直接放在每个声明上方。
  • 声明 LWC 模块命名空间 c/componentName(如果组织使用不同的命名空间,则使用该命名空间)。

模板:将 [[assets/dts-template.ts|assets/dts-template.ts]] 加载为起始 .d.ts 结构。

如果组件没有 @api 成员,仍需生成模块声明,并添加注释说明没有公开接口——不要跳过该文件。

步骤 5 — 编译和测试
  • 运行 TypeScript 编译器(tsc --noEmit 或构建中的等效命令)。完成前解决所有错误;不要使用 @ts-ignore 补丁。
  • 运行组件现有的 Jest 测试。行为应保持一致。
  • 无条件运行随附的消费者查找脚本——输出为空是有效结果,不能作为跳过的理由。脚本会从 sfdx-project.json 的 packageDirectories 解析搜索路径(若不存在则回退到 <project-root>),拒绝任何超出项目根目录的条目,并在内部执行 LWC 导入搜索,因此调用结果完全确定:
"<skill_dir>/scripts/find-consumers.sh" "<project-root>" "<componentName>"

对于每个匹配项,确认消费者所需的类型仍与新的 .d.ts 公开接口一致。

步骤 6 — 预期的最终打包结构
componentName/
├── componentName.ts          # Main TypeScript implementation
├── componentName.html        # Template (unchanged)
├── componentName.css         # Styles (unchanged)
└── componentName.d.ts        # Type definitions (new)

验证清单

转换前:

  • 组件是有效的 JS,且所有测试均通过。
  • 已识别每个 @api 成员及其预期类型。

转换后:

  • 使用了 git mv,以保留历史记录。
  • .ts 中的每个变量和参数都有明确类型(没有隐式 any)。
  • 复杂对象结构应放在 interface / type 别名中,而不是重复内联编写。
  • 仅在字段确实可选时使用 ?。
  • .d.ts 存在,声明了 c/componentName,继承 LightningElement,且仅包含 @api 成员。
  • .d.ts 中原样保留了每个 @api JSDoc。
  • tsc 通过且没有错误;没有使用 @ts-ignore,也没有将 any 用作变通方案。
  • Jest 测试仍然通过。

常见陷阱

  • 使用 any 来屏蔽错误。 应解决实际的类型问题。如果值确实未知,请使用 unknown 和类型守卫。
  • 在 .d.ts 中包含私有成员。 .d.ts 是公开契约。内部生命周期钩子和辅助方法不得泄漏。
  • 重命名时丢失 JSDoc。 重命名前后都要检查——.ts 和 .d.ts 中都必须包含 @api 成员的 JSDoc 注释。
  • 跳过 git mv。 这会让审查变得困难,也会干扰 blame。
  • 忘记异步返回类型。 带有 async 关键字的 foo() 总是返回 Promise。请声明这一点。
  • 将 onclick 声明为 PointerEvent。 click 是 MouseEvent(包括键盘触发的点击),因此这类事件的 PointerEvent 字段(如 pointerType)未定义。将 onclick 声明为 MouseEvent;PointerEvent 仅用于 onpointer* 处理程序。只有当代码明确区分处理 TouchEvent 时,才使用 MouseEvent | TouchEvent。

支持资源

评论

支持 Markdown