十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

plotly.js TypeScript 类型系统:从属性 schema 到生成 .d.ts 的架构与迁移实践

plotly.js TypeScript 类型系统:从属性 schema 到生成 .d.ts 的架构与迁移实践 plotly.js TypeScript 类型系统从属性 schema 到生成 .d.ts 的架构与迁移实践【免费下载链接】plotly.jsOpen-source JavaScript charting library behind Plotly and Dash项目地址: https://gitcode.com/GitHub_Trending/pl/plotly.jsplotly.js 正处于一场系统性的 TypeScript 化进程中src/types/目录是这场工作的文档中枢它定义了三层类型架构消费者面、内部创作面、schema 生成面、由plot-schema.json驱动的类型生成器以及把各组件attributes.js逐个迁移为attributes.ts的标准配方。读完本文你将理解 plotly.js 公共类型是如何从运行时 schema 自动派生的、npm run typecheck与schema-typegen-diff-check两道 CI 门禁的原理以及贡献者如何安全地完成一次属性文件迁移。一、类型系统文档总览README 将src/types/下的四份配套文档按读者角色划分文档目标读者SETUP.md首次贡献者 —— 工具链概览、npm scriptsARCHITECTURE.md所有接触类型的人 —— 目录布局、公共/私有切分CONVERTING_ATTRIBUTES.md正在做转换工作的贡献者—— 分步配方GENERATOR.md需要扩展或调试类型生成器的维护者当前迁移状态以 README 的 Status 小节为准TypeScript 构建基础设施✅ 已完成src/types/中的公共类型面✅ 已完成AttributeMap校验机制✅ 已完成基于 schema 的类型生成器✅ 已完成 —— 覆盖全部 trace 类型 layout 共享接口消费者入口lib/index.d.ts通过package.json#types接线✅ 已完成CI 门禁typecheckschema-typegen-diff-check✅ 已完成首个属性文件转换modebar✅ 已完成其余文件转换 进行中二、三层架构消费者面、创作面与真相来源ARCHITECTURE.md 把类型系统划分为三层消费者面npm install plotly.js后所见lib/index.d.ts通过 package.json 的types: ./lib/index.d.ts接线。它对生成类型使用export type *再显式重导出手写类型并以export as namespace Plotly支持全局/命名空间用法。内部创作面src/types/index.d.ts是重导出枢纽——lib/common、core/api、core/config、core/events、core/layout加上三个.internal文件与生成类型。类型来源分两支生成类型src/types/generated/运行时 schema 的权威 TypeScript 表示。真相链是属性文件src/.../attributes.js→plot-schema.json→ 生成类型手写类型src/types/中除generated/外的所有文件覆盖 schema 描述不到的部分——事件、内部运行时状态、公共 API 函数签名、工具类型Color、Datum、MarkerSymbol、ErrorBar、行为类型ModeBarButton、Icon等。公共与私有下划线约定Plotly 在图元素上存两种状态用户传入的公共数据Plotly.newPlot(gd, data, layout)接受的以及应用默认值后私有_前缀的完全解析版本。类型体系同样镜像这一切分面向用户内部定义位置LayoutFullLayoutgenerated/schema.d.ts中的Layoutcore/layout.internal.d.ts中的FullLayoutData按type区分的联合FullDatagenerated/schema.d.ts中的DataFullData Data FullDataInternals位于core/data.internal.d.ts无对应GraphDivgd参数core/graph-div.internal.d.ts—— 带_fullLayout、_fullData、calcdata等的 DOM 元素FullData是 schema trace 类型与内部_前缀字段的交集联合因此内部代码按trace.type收窄时一次表达式中同时得到 trace 特有字段与内部状态。内部类型有意使用宽泛的索引签名[key: string]: any以便增量迁移不被类型错误阻塞当 JS→TS 转换中发现新的_属性时把它补进FullLayout或FullDataInternals即可见 ARCHITECTURE.md 中Adding internal properties一节的示例。带.internal.d.ts后缀的文件data.internal.d.ts、graph-div.internal.d.ts、layout.internal.d.ts中的类型不属于公共 API不会在lib/index.d.ts中重导出没有该后缀的文件则其全部导出均为公共。目录布局src/types/ ├── index.d.ts # 主重导出枢纽公共 内部 ├── core/ # 核心 API 的手写类型 │ ├── api.d.ts # 公共 API 函数签名newPlot 等 │ ├── config.d.ts # Config、ToImgopts │ ├── data.internal.d.ts # CalcData、FullData │ ├── events.d.ts # PlotMouseEvent、PlotlyHTMLElement 等 │ ├── graph-div.internal.d.ts # GraphDiv、GraphContext │ ├── layout.d.ts # AxisName、ModeBar 行为类型、Template │ └── layout.internal.d.ts # FullLayout、LayoutSize、SubplotInfo ├── lib/ # 原语 schema 校验机制 │ ├── common.d.ts # Color、Datum、TypedArray、MarkerSymbol… │ └── attributes.d.ts # AttributeMap、AttrInfo编译期校验 └── generated/ # 机器生成的类型 └── schema.d.ts # 全部 trace layout 共享类型三、工具链esbuild 负责构建tsc 负责验证SETUP.md 明确了分工esbuild 原生支持.ts文件去类型、转译打包无需额外插件tsconfig.json 设置noEmit: truetsc 从不写文件——esbuild 是构建系统tsc 是验证器。关键配置事实以仓库实际内容为准tsconfig.jsontarget: ES2016、strict: true对.d.ts声明与已转换的 TS 源全量严格、allowJs: truecheckJs: false其余 JS 文件宽松共存、moduleResolution: bundler、isolatedModules: trueinclude 范围为src/**/*、lib/**/*、tasks/**/*。package.json 中与类型工作相关的 devDependenciestypescript仅用于类型检查、types/node、types/d3esbuild-config.js 为打包配置与 tsconfig 同为 ES2016 目标。核心 npm scriptsnpm run typecheck # tsc --noEmit报告错误无产物 npm run typecheck-watch # 增量重检 npm run schema # 重建 test/plot-schema.json 重新生成 src/types/generated/ 下类型 npm run schema-typegen-diff-check # 重新生成并确认 test/plot-schema.json 与 src/types/generated/schema.d.ts 无变化 npm run build # 完整生产构建典型工作流开发期一个终端跑npm run typecheck-watch另一个终端跑npm startdev server提交前跑npm run typecheck若改动了属性文件再跑npm run schema。CI 将两项检查作为独立任务运行。一个容易踩坑的细节当 JS 文件require()一个带默认导出的 TS 文件时esbuild 的 CommonJS 互操作会把结果包成{ default: ... }因此消费者必须写require(./attributes).default。这个模式在把attributes.js转成attributes.ts时会立刻显现详见下文配方第 4 步。四、schema 类型生成器从 plot-schema.json 到 schema.d.ts生成器 tasks/generate_schema_types.mjs 读取 test/plot-schema.json向 src/types/generated/schema.d.ts 输出全部 schema 派生类型公共枚举别名Calendar、Dash、AxisType、PatternShape、XRef、YRef、TransitionEasing、TraceType外加已弃用的PlotType别名每种 trace 的数据接口BarData、ScatterData、IndicatorData等与覆盖全部 trace 的Data判别联合经type字段收窄Layout 组件接口LayoutAxis、Legend、Scene、Annotation、Shape、Slider、UpdateMenu等及Layout本身共享子接口Font、ColorBar、HoverLabel、LegendGroupTitle等动画/帧/编辑接口AnimationOpts、Frame、Edits_internal命名空间收纳不宜直接作为公共面的类型如_internal.Marker、_internal.AutoRangeOptions。运行npm run schema即可重新生成。GENERATOR.md 描述了完整内部机制阶段 0公共枚举发现discoverCommonTypes(schema)遍历 schema把 key/path/values 与COMMON_TYPE_ANCHORS条目匹配的枚举属性识别为公共别名。多个站点命中同一锚点时如 3D 场景轴与笛卡尔轴都有xaxis.type取最大值集作为别名体保证别名足够宽松。TraceType是特例从Object.keys(schema.traces)派生而非来自属性同时输出/** deprecated */ export type PlotType TraceType;以兼容旧导入。阶段 1–2指纹与共享接口提取对 trace 和 layout 中每个容器子树做指纹排序后的 key 叶子valType构成指纹串。出现次数 ≥MIN_OCCURRENCES且属性数 ≥MIN_PROPERTIES的容器提取为共享接口Font、ColorBar、HoverLabel 等PascalCase 命名由SHARED_NAME_OVERRIDES控制colorbar→ColorBar而非Colorbar且被点名覆盖的容器可绕过MIN_PROPERTIES门槛。指纹完成后生成器还会注入schema.animation的transition/frame子树为共享类型Transition、AnimationFrameOpts——它们出现次数不足自动提取阈值但需要命名以便AnimationOpts干净地引用。阶段 3–5trace、layout、动画接口每个 trace 得到一个接口ScatterData、BarData等指纹匹配处引用共享类型随后输出判别联合Data PartialBarData | PartialBarpolarData | …。schema 中增删 trace 会自动反映到该联合。Layout 生成处理三类容器subplot 容器_isSubplotObj标记按目标名分组并合并为超集如xaxis/yaxis都映射到LayoutAxis链接到数组的容器{items: {name: {...}}}形态提取为命名接口如 Annotation、Shape在 Layout 中呈数组形式annotations?: Annotation[]普通容器内联或引用共享类型。Layout 接口含 subplot 索引签名模板字面量键如[key: \xaxis${number}]: LayoutAxis。AnimationOpts来自schema.animationFrame来自schema.frames.items.frames_entry递归字段用field overrides覆盖 schema 中的valType: anyattrsToProperties(frameEntry, , frame, sharedTypes, { data: any[], layout: PartialLayout });Edits从schema.config.edits输出全部为具体布尔字段ConfigBase从schema.config输出其中 6 个valType: any的字段locales、modeBarButtons等以any通过由 core/config.d.ts 中手写的Config用OmitConfigBase, keyof ConfigOverrides ConfigOverrides覆盖之。阶段 6_internal命名空间INTERNAL_INTERFACES中的名字AutoRangeOptions、ErrorY、Lighting、Line、Marker被包进export namespace _internal { ... }而非顶层输出。命名空间外引用它们时生成器加_internal.前缀如ScatterData.marker?: _internal.Marker命名空间内部则用裸名。这样import { Marker } from plotly.js会失败——推荐路径是索引访问ScatterData[marker]。之所以用命名空间而不是简单的不导出TypeScript.d.ts文件语义会让非导出的顶层声明透过export *泄漏出去一个为兼容手写 DefinitelyTyped 而保留的历史怪癖把名字变成非顶层即消除了泄漏。valType → TypeScript 映射valType生成的 TSdata_arrayDatum[] \| TypedArraynumber/integernumberextras以字面量追加如number \| autostring有values时为字面量联合命中公共枚举别名时引用别名否则stringbooleanbooleancolorColorcolorscaleColorScalecolorlistColor[]anglenumber \| autosubplotidstringenumeratedvalues的字面量联合命中公共枚举别名时引用别名flaglistflags extras 的联合 (string {})允许组合同时保留自动补全info_array定长时为元素valType的元组freeLength时为T[]兜底any[]anyanyarrayOk: true会把结果包成T \| T[]ATTR_NAME_OVERRIDES则按属性路径强制映射到特定类型别名如marker.symbol→MarkerSymbol。输出中被剥离的元数据键来自schema.defs.metaKeyseditType、role、description、impliedEdits、_isSubplotObj、_isLinkedToArray、_arrayAttrRegexps、_deprecated将来新增会被自动纳入。formatJSDoc(attr, indent)为每个叶子属性输出多行 JSDoc 块包含 schema 的description、default、数值边界min/max及impliedEditsPlotly 的*emphasis*标记原样保留在 IDE 悬浮提示中渲染为斜体描述中的*/会被转义以免提前关闭注释。五、属性文件迁移配方as const satisfies AttributeMapCONVERTING_ATTRIBUTES.md 是贡献者当前活跃工作流的核心。属性文件是运行时 schema 的真相来源转换不改变这条链——它在其上叠加编译期校验没有as const satisfies AttributeMap结构错误的属性对象缺valType、values拼写错误、dflt形状不对只能在运行时才暴露转换后这些结构性错误在 TypeScript 编译期即被捕获。校验机制定义在 src/types/lib/attributes.d.tsAttrInfo是按valType判别的 12 路联合DataArrayAttr、NumberAttr、IntegerAttr、StringAttr、BooleanAttr、ColorAttr、ColorScaleAttr、ColorListAttr、AngleAttr、SubplotIdAttr、EnumeratedAttr、FlagListAttr、InfoArrayAttr、AnyAttr其中EnumeratedAttrV用泛型把dflt约束为V[number]——这正是忘记给values加as const就查不出dflt不在values里的关键。标准配方小文件约 10 分钟复杂 trace 约一小时重命名并声明导入src/path/attributes.js→attributes.ts顶部加import type { AttributeMap } from ../../types/lib/attributes;相对路径按实际位置调整。转换导出module.exports { ... };换成const attributes { // ... 原有属性定义 } as const satisfies AttributeMap; export default attributes;as const保留values: [v, h]这类字面量类型satisfies AttributeMap在不做宽化的前提下校验结构。数组字面量补as const否则values宽化为string[]dflt失去必须是values之一的约束。更新消费者所有require(./attributes)的 JS 文件改为require(./attributes).defaultesbuild CommonJS 互操作要求。核对生成类型trace 与 layout 组件的消费者类型由生成器从plot-schema.json产出转换后确认src/types/generated/schema.d.ts中已存在对应类型即可——转换本身的价值是对源做类型检查。若手写类型有而 schema 生成类型没有的属性多半是运行时内部状态应加入相应Full*接口而非生成侧覆盖。验证npm run typecheck # 零错误 npm run schema-typegen-diff-check # 重新生成并确认 test/plot-schema.json # 与 src/types/generated/ 无变化package.json 中该脚本即npm run schema git diff --exit-code src/types/generated/ test/plot-schema.json。一次正确的转换产生字节级一致的 schema若两个文件出现 diff说明属性对象的运行时形状变了最常见是漏了as const或拼写错误此时应与原.js文件逐字符比对。提交每个文件一次自包含提交正确的转换在src/types/下无需提交任何内容。范例modebarCONVERTING_ATTRIBUTES.md 指认 src/components/modebar/attributes.ts 为规范范例。实际文件印证了配方顶部import type { AttributeMap } from ../../types/lib/attributesorientation用values: [v, h] as const且dflt: h受V[number]约束整个对象以as const satisfies AttributeMap收尾。注意modebar 等所有 layout 组件的消费者类型都由生成器产出attributes.ts转换的价值在于用AttributeMap类型检查源定义本身。若 schema 生成的类型过松正确做法是回到 JS 属性源修改让所有语言移植受益而非在生成器侧覆盖仅当 schema 无法自引用如递归的Frame.data时才使用fieldOverrides。并行转换的优先级清单贡献者可从以下清单认领文件PR 描述中认领每次转换单独提交因各自限定在一个组件目录及其直接require()调用者内合并冲突很少Tier 1小而简单modebar已完成规范范例、src/components/rangeslider/attributes.js、src/plots/gl3d/layout/attributes.js极小仅一个subplotid、src/plots/cartesian/attributes.js、src/components/fx/attributes.js。注意src/components/color/attributes.js名为属性文件但实际只导出颜色常量不走本配方。Tier 2中等sliders、updatemenus、rangeselector、colorbar 属性文件。Tier 3layout 本身。六、进行中的 TODO 与已知取舍README 列出了两项开放的转换 TODOsrc/fonts/ploticon.js转换它使 src/types/core/api.d.ts 中的DefaultIcons与IconsMap能从模块派生type DefaultIcons keyof typeof Ploticon取代当前手工维护、可能漂移的联合类型消费者需按既有模式见 CONVERTING_ATTRIBUTES.md 的.default互操作约定追加.default。为data_array增加维度信息schema 的data_arrayvalType 不携带形状信息但部分属性确为 2Dheatmap/contour/contourcarpet 的z、surface 的z与surfacecolor、这些 trace 上的 2Dtext/customdata/hovertext或 3Dimage.z。生成器目前对所有data_array输出宽松联合Datum[] | Datum[][] | TypedArray使 2D/3D 用法都能通过类型检查代价是纯 1D 字段也接受 2D 数组。此外GENERATOR.md 还记录了一个有意未实现的取舍flaglist若展开成全组合联合x | xy | xytext | …hoverinfo将产生 15 成员的大类型并拖慢类型检查因此当前采用(string {})方案保留自动补全。七、调试与扩展要点速查调试生成输出npm run schema重新生成后检查schema.d.tsnpm run typecheck看 tsc 判定也可直接require(test/plot-schema.json)检查s.layout.layoutAttributes.xaxis、s.traces.scatter.attributes等原始结构。新增公共枚举别名在COMMON_TYPE_ANCHORS加namematch(key, path, values)谓词重新生成后引用会自动改写。新增 layout 容器subplot 类型加进LAYOUT_CONTAINER_NAMES数组容器加进LAYOUT_ARRAY_NAMES。隐藏类型进_internal把名字加入INTERNAL_INTERFACES生成器会自动包裹并改写外部引用。适用场景名字会误导消费者如Marker只是 scatter 的变体、schema 内部辅助类型、或被手写类型取代ErrorY因公共面首选ErrorBar而隐藏。JSDoc 约定导出的 TS 函数使用顶层 JSDoc 块param name - description用连字符分隔、省略类型签名已有。适用前提以上脚本与配置均基于当前仓库plotly.js 4.0.0要求 Node 22。生成类型只覆盖plot-schema.json描述的属性事件、运行时行为与内部状态始终由手写类型承担二者边界由.internal.d.ts后缀约定固化。【免费下载链接】plotly.jsOpen-source JavaScript charting library behind Plotly and Dash项目地址: https://gitcode.com/GitHub_Trending/pl/plotly.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表