Carbon Design System 组件 TypeScript 类型接入指南:为 @carbon/react 渐进式添加组件类型定义

📅 发布时间:2026/9/16 18:11:52
Carbon Design System 组件 TypeScript 类型接入指南:为 @carbon/react 渐进式添加组件类型定义
Carbon Design System 组件 TypeScript 类型接入指南为 carbon/react 渐进式添加组件类型定义【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon导读本文基于 IBM Carbon Design System 仓库中的 adding-component-types.md 指南系统讲解carbon/react组件 TypeScript 类型定义的目标、策略与落地步骤。你将掌握如何在不把整个代码库迁移到 TypeScript的前提下以最低成本为公开 API 组件补齐类型定义、如何复用 DefinitelyTyped 上的旧版类型、如何用// ts-check与 JSDoc 保护内部文件以及如何通过 Public API 快照测试与版本化策略评估类型改动的影响。文中所有操作步骤均对照当前仓库中的真实源码如 Button.tsx、Checkbox.tsx展开可直接复用于你的贡献流程。目标Goal以最小 TypeScript 投入换取最大下游收益本工作流的核心目标用一句话概括就是尽可能快地为使用 TypeScript 的消费者提供下游价值同时只写尽可能少的 TypeScript。明确地说这个目标不是把整个代码库立刻转换成 TypeScript。Carbon Design System 的开发者社区体量庞大大多数项目并不使用 TypeScript贡献者对 TypeScript 的熟练程度参差不齐。为了让贡献门槛尽量低从而促进实验、创新与系统演进当前阶段必须严格控制仓库内 TypeScript 的体量。从仓库现状可以印证这一策略packages/react/src/components/下大量组件文件已经完成.tsx化例如 Button.tsx、Checkbox.tsx而内部工具函数仍保留.js后缀并借助// ts-check获得基本的类型检查能力。目的Purpose为什么要做这件事为组件补充类型定义预期带来三类收益开发者生产力提升组件 API 具备自文档化能力与代码编辑器的智能提示intellisense深度集成消费方无需翻阅文档即可获得准确的属性提示。产品质量提升由carbon/react官方直接提供的类型定义比第三方维护的类型更稳定、更正确、更完整。维护成本降低不再需要经过 DefinitelyTyped 的提交流程与系统来维护类型类型定义与组件实现同仓库演进、同步发布。同时也要正视代价引入 TypeScript 对社区贡献者来说是一个不小的转变即便是最小的 PR 也会因此增加摩擦。Carbon 团队相信类型定义的大部分收益可以在不转换整个代码库的前提下传递给消费者因此整体工作将聚焦于把仓库内的 TypeScript 数量控制在必要范围内。策略Strategy增量采纳、公开 API 优先TypeScript 将被增量采纳优先为carbon/react公开 API 所包含的组件 prop API 添加类型。明确边界哪些暂时不做内部组件、辅助函数等不参与公开 API 的部分保留.js后缀在文件顶部通过// ts-check开启错误检查并对需要显式类型的位置使用 JSDoc 类型标注/** type any */。其他包如carbon/icons-react、carbon/elements等当前阶段同样不做类型化处理。从源码角度看packages/react/tsconfig.json中的include: [src/**/*, icons/src/index.ts]也印证了类型检查范围目前集中在 react 主包的src目录。类型与 semver 的关系当前不绑定增量采纳策略下现阶段类型定义不与 semver 绑定即类型按原样提供as-is basis。理想情况下类型是稳定且不引入破坏性变更的但现实是类型可能偶发错误、过时甚至缺失。这一约定在 versioning.md 中有完整阐述文档的变更对照表中明确写着组件类型/定义发生变化 →patch级版本号并在示例章节进一步说明组件 API 的类型定义按原样提供、不与 semver 绑定包括类型的增删改都可能跨越 patch 或 minor 版本出现破坏性变更。因此消费方在升级依赖时不应假设类型永远向后兼容如果发现类型过时或错误应通过 issue 反馈或直接提交 PR 修复。为组件提供基线类型定义的步骤仓库为每个待转换组件给出了一套通用操作流程当前进度由 issue #12513 跟踪以下是完整步骤与源码印证。1. 通过 git 重命名文件为.tsx使用git mv而非普通重命名以保留文件的历史记录git mv packages/react/src/components/ComponentName/ComponentName.js packages/react/src/components/ComponentName/ComponentName.tsx例如将Button.js迁移为 Button.tsx文件内同时包含类型定义与组件实现。2. 复制 propTypes 定义到组件定义之上把现有的 propTypes 定义复制到组件定义上方作为后续改造为 ts interface 的素材。3. 将 propTypes 改造成 ts interface这是核心转换步骤。对照仓库中的真实实现可以看到两种典型模式模式一直接声明 interface 字面量联合类型。以 Button.tsx 为例先用具名常量数组声明枚举值再派生类型export const ButtonKinds [ primary, secondary, danger, ghost, danger--primary, danger--ghost, danger--tertiary, tertiary, ] as const; export type ButtonKind (typeof ButtonKinds)[number]; export const ButtonSizes [xs, sm, md, lg, xl, 2xl] as const; export type ButtonSize (typeof ButtonSizes)[number];随后在 interface 中引用这些类型例如size?: ButtonSize;。这种常量数组 as const 索引访问类型的写法既提供了运行时可用的数组可继续用于PropTypes.oneOf又派生了编译期类型。模式二extends 原生 HTML 属性 Omit 剔除冲突字段。以 Checkbox.tsx 为例type ExcludedAttributes id | onChange | onClick | type; export interface CheckboxProps extends OmitReact.InputHTMLAttributesHTMLInputElement, ExcludedAttributes { id: string; labelText: NonNullableReactNode; onChange?: ( evt: React.ChangeEventHTMLInputElement, data: { checked: boolean; id: string } ) void; }这里把原生input的属性全部继承仅剔除需要自定义签名或必填化的id、onChange、onClick、type然后重新定义必填的id、labelText以及携带自定义回调签名的onChange。这种写法既保证了与 DOM 属性的一致又对 Carbon 特有的语义做了精确约束。对于支持as属性的多态组件polymorphic仓库还提供了现成的工具类型 PolymorphicProps.ts如PolymorphicComponentPropWithRefC, PropsButton.tsx 即通过它组合出ButtonProps与ButtonComponent类型。4. 检索 DefinitelyTyped 上的旧版类型并作为起点在 DefinitelyTyped 中检索该组件此前由社区提供的旧类型将旧类型复制到工作文件中作为类型定义的起点注意旧类型最初是为carbon-components-reactv7.x提供的可能已经过时即便如此也应尽量让carbon-components-reactv7.x的旧类型与为carbon/react1.x提供的新类型保持最大程度的一致性parity复制后需核对旧类型是否需要更新以匹配当前组件的 propTypes 规格。5. 修复出现的错误在将 propTypes 改造为 interface、并整合旧类型后修复 TypeScript 编译与类型检查中暴露的所有错误。6. 不为公开 API 之外的内部文件添加类型严格遵循边界内部文件保留.js后缀需要显式类型的位置使用 JSDoc 标注/** type any */在文件第一行添加// ts-check开启该文件的类型错误检查。这样内部文件在获得一定类型安全性的同时不引入整体转换的成本。7. 测试你的改动在类型改造完成后需要验证类型定义确实可用。官方推荐两种验证方式在文件底部编写一个使用该组件的 dummy 组件确保你可以传入所有需要的 props 而不会出现类型错误取一个该组件的 Storybook 示例复制粘贴到.tsx文件底部验证示例中的 props 是否都能通过你定义的 interface 校验。8. 提交 PR保持 PR尽可能小除非必要避免在单个 PR 中塞入多个组件在 PR 描述中使用关键字关联并关闭跟踪 issue例如Closes #12513遇到问题可以随时在 Slack#carbon-wg-typescript频道、Discord 或对应 issue 中提问。常见问题FAQ与仓库实证如何判断什么属于公开 API两条权威判定途径Storybook如果组件没有出现在 carbon/react 的 Storybook 中它很可能不是公开 API。Public API 快照整个公开 API 都被快照跟踪。快照文件位于 PublicAPI-test.js.snap如果组件不在快照中它就不属于公开 API。快照由 PublicAPI-test.js 生成测试通过 mockprop-types包遍历carbon/react入口导出将每个组件的 propTypes 信息序列化为快照。因此当 propTypes 更新后需要在项目根目录运行yarn test -u重新生成快照并随 PR 一起提交审查。该测试的注释明确指出快照失败意味着公开 API 发生了变化需要对应的 semver 变更这帮助核心审查者判断 API 变更是否向后兼容。组件是否应同时保留 propTypes 与 ts interface是。两者并存propTypes 负责运行时校验React 开发模式下ts interface 负责编译期校验与编辑器提示。以 Button.tsx 为例组件上方定义ButtonBasePropsinterface组件下方仍然完整保留Button.propTypes含PropTypes.oneOfType、自定义校验函数等运行时逻辑。注释文档是否要复制到 ts interface 中是。原因在于当前 Storybook 的文档生成仍优先使用 react-docgen其读取的是 propTypes 上的注释只有等所有组件都具备 ts interface 之后才能将 Storybook 配置切换为优先使用 TypeScript 文档。因此在过渡期内注释需要在 propTypes 定义与 ts interface两处重复维护。观察 Checkbox.tsx 可以看到 interface 中的每个属性都带有与 propTypes 对应的 JSDoc 注释。ts interface 应该放在文件中的什么位置放在组件定义上方通常是文件顶部。最终的文件结构布局是┌─────────────────────────────────────┐ │ ts interface类型定义文件顶部 │ ├─────────────────────────────────────┤ │ 组件实现夹在中间 │ ├─────────────────────────────────────┤ │ propTypes 定义组件下方 │ └─────────────────────────────────────┘即组件实现被夹在 ts interface 与 propTypes 定义之间。这一布局在 Checkbox.tsxinterface 在前、const Checkbox React.forwardRef(...)居中、propTypes 在底部与 Button.tsx 中都能得到验证。总结Carbon Design System 对组件类型定义采取的是务实的渐进式路线类型只覆盖公开 API内部文件用// ts-check兜底propTypes 与 ts interface 双轨并存以兼容 Storybook 文档生成类型不与 semver 绑定、按原样提供Public API 快照测试确保任何 API 变动都进入审查视野。对贡献者而言只要遵循git mv 保留历史 → propTypes 改造 interface → 复用 DefinitelyTyped 旧类型 → 底部 dummy 组件验证 → 小 PR 提交这条路径就能以最小的成本持续提升carbon/react的 TypeScript 体验。【免费下载链接】carbonA design system built by IBM项目地址: https://gitcode.com/GitHub_Trending/carbo/carbon创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考