Handsontable TypeScript 类型系统完全指南:声明入口、类型参考与实战类型安全
2026/9/21 16:20:56 网站建设 项目流程
  • 前端
  • UI组件

【免费下载链接】handsontable

JavaScript Data Grid / Data Table with a Spreadsheet Look & Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡

项目地址:https://gitcode.com/gh_mirrors/ha/handsontable
点击查看免费下载

Handsontable 为整个公共 API 提供了开箱即用的 TypeScript 声明文件(.d.ts),本文是官方类型参考文档的完整展开版:涵盖支持的最低 TypeScript 版本与声明降级机制、handsontablehandsontable/base两个类型入口的选择、从配置类型到主题类型/Overlay 类型的全量类型清单,以及 settings 对象、hook 回调、自定义 renderer/editor、React/Angular/Vue 实例引用的实战写法。读完本文,你将能在自己的 Handsontable 项目中获得完整的类型检查与 IDE 自动补全,并理解这些声明在仓库中是如何生成与维护的。

支持的 TypeScript 版本

官方发布的.d.ts文件要求TypeScript 5.1 或更高版本。如果项目使用低于 5.1 的 TypeScript,请先升级 TypeScript 版本,再使用这些声明。

Handsontable 内部使用最新的 TypeScript 版本进行开发与编译(当前仓库的 handsontable/package.json 中开发依赖为typescript: "^6.0.0"),以获得最严格的内部类型检查和最新的语言特性。但发布出去的声明文件会被降级(downlevel)到 TS 5.1 兼容级别:构建时,脚本会把只存在于 TS 5.2+ 的类型(WeakKeyDisposableAsyncDisposable)替换为 TS 5.1 等价物,把只存在于 TS 5.6+ 的类型(ArrayIteratorIteratorObject等)替换为对应的迭代器类型。

这一降级过程由 handsontable/scripts/downlevel-dts.mjs 实现,其注释明确写着:"The published .d.ts must be consumable by TypeScript 5.1+. The dev compiler may be a newer TS version — only the output is downleveled."(发布的 .d.ts 必须能被 TS 5.1+ 消费;开发编译器可以更新,只有输出被降级)。脚本的具体替换规则包括:

源类型(较新 TS)替换为(TS 5.1 兼容)所属版本
ArrayIterator/MapIterator/SetIterator/StringIteratorIterableIteratorTS 5.6
BuiltinIteratorReturnanyTS 5.6
IteratorObject<...>IterableIterator<T1>(保留第一个类型参数,见GENERIC_REPLACEMENTSTS 5.6
AsyncIteratorObject<...>AsyncIterableIterator<T1>TS 5.6
WeakKeyobjectTS 5.2
Disposable/AsyncDisposableobjectTS 5.2

注意IteratorObject这类泛型不能简单用正则替换——其第一个类型参数本身可能包含嵌套尖括号(如IteratorObject<Map<K, V>, undefined>),所以 downlevel-dts.mjs 用括号深度计数(bracket depth counting)来定位第一个类型参数的边界,再折叠多余参数。

另外需要了解:在未来的主版本中提高最低支持的 TypeScript 版本对使用者而言属于破坏性变更(breaking change),任何此类变更都会在该版本的迁移指南中明确说明。因此在升级 Handsontable 主版本前,务必留意迁移指南中的版本要求说明。

类型入口:两个入口点

Handsontable 从两个入口点暴露类型,两者提供完全相同的类型,选择哪个取决于你如何导入库本身:

完整包(Full package)——所有模块已预先注册,不做 tree shaking:

import Handsontable, { GridSettings, HotInstance } from 'handsontable';

基础包(Base package)——当你只导入需要的模块时使用:

import Handsontable, { GridSettings, HotInstance } from 'handsontable/base';

这些入口的类型映射在 handsontable/package.json 的exports字段中有明确声明:.(根入口)的import条件指向./index.d.mtsrequire条件指向./index.d.ts./base入口同理指向./base.d.mts/./base.d.ts。也就是说,无论是 ESM 还是 CommonJS 消费者,都能拿到对应的类型声明。

对于下面列出的所有类型,也都可以使用 TypeScript 的import type语法,它不会产生任何运行时输出:

import type { GridSettings, HotInstance } from 'handsontable';

此外,仓库还单独导出了handsontable/settings子路径(见 handsontable/package.json),核心设置类型如RowObjectCellValueCellChangeChangeSourceColumnSettingsCellMetaCellProperties等都从这里对外再导出,外部消费者可以直接import ... from 'handsontable/settings'(对应源码 handsontable/src/settings.ts 的注释说明)。

类型参考(Type reference)

配置类型(Configuration types)

这些类型描述传入new Handsontable()updateSettings()的设置对象:

类型说明
GridSettings所有 Handsontable 配置项。用于顶层设置对象。
ColumnSettings列级覆盖。columns数组中的每一项使用该类型。
CellProperties全局 → 列 → 单元格级联合并后的单元格级设置。渲染时只读。
CellMeta可变(mutable)的单元格级元数据,存储在hot.getCellMeta()中。继承CellProperties
Events所有 hook 回调签名,以 hook 名为键。用于为单个 hook 函数标注类型。
SanitizerContext网格传给sanitizer选项的写入面(write surface)。用它标注选项的第二个参数可获得这些值的补全。
TextExtractorContext网格传给textExtractor选项的消费面。用它标注选项的第二个参数可获得这些值的补全。
import type { GridSettings, ColumnSettings } from 'handsontable'; const columns: ColumnSettings[] = [ { data: 'name', type: 'text' }, { data: 'revenue', type: 'numeric', numericFormat: { style: 'currency', currency: 'USD', minimumFractionDigits: 2, maximumFractionDigits: 2 } }, ]; const settings: GridSettings = { data: myData, columns, licenseKey: 'non-commercial-and-evaluation', };

从源码结构看,GridSettings定义在 handsontable/src/core/settings.ts,它带有一个宽泛的索引签名([key: string]: any)以便允许任意插件/元数据键。正因如此,handsontable/src/settings.ts 中定义ColumnSettings时使用了Omit<RemoveIndexSignature<GridSettings>, 'data'>:先用RemoveIndexSignature剥掉索引签名,再执行Omit,否则Omit会把所有命名选项塌缩成裸索引签名(所有读取都变成any,自动补全消失)。ColumnSettings重新声明了data?: string | number | ColumnDataGetterSetterFunction,使其语义更贴合"每列一个 data"。

CellMeta在 handsontable/src/settings.ts 中扩展了ColumnSettings,额外加入classNamereadOnlyvalidcommentisSearchResulthiddenskipRowOnPaste等单元格级属性;CellProperties(同文件 L112-L119)再叠加渲染期计算出的rowcolinstancevisualRowvisualColprop字段。

SanitizerContext在 handsontable/src/core/settings.ts 中定义为字符串联合:'header' | 'password' | 'contextMenu' | 'selectEditor' | 'dialog' | 'notification' | 'CopyPaste.paste' | 'CopyPaste.paste.sourceData' | (string & {})(string & {})这一成员是为了兼容调用方自定义 sanitizer、与其他库共用 sanitizer 或未来新增写入面的场景——代价是类型无法拒绝拼写错误的值。

数据类型(Data types)

这些类型描述进出网格的值:

类型说明
CellValue单个单元格的值:string \| number \| boolean \| null \| undefined
CellChange单个变更元组:[row, column, oldValue, newValue]。传给afterChangebeforeChange
RowObject以普通对象表示的数据行,当data是对象数组时使用。
ChangeSource所有内置变更来源标识符的字符串联合(如'edit''loadData''UndoRedo.undo')。
SourceRowData任何索引转换之前的原始源数据行。
ColumnDataGetterSetterFunctiondata选项作为 getter/setter 函数的形态。使用函数数据源时,变更 hook 会以它作为prop传入。
SelectOptionsObjectselectautocomplete单元格类型的选项条目:{ value, label }
RangeType单元格范围描述符:{ from: CellCoords, to: CellCoords }
import type { CellChange, ChangeSource } from 'handsontable'; function onAfterChange(changes: CellChange[] | null, source: ChangeSource): void { if (!changes || source === 'loadData') return; for (const [row, col, , newValue] of changes) { console.log(`Cell [${row}, ${col}] changed to`, newValue); } }

源码层面,这些类型的精确定义位于 handsontable/src/settings.ts:

  • CellValue = unknown(L19):文档说明它"可以是任何值以支持自定义单元格数据类型,但默认是string | number | boolean | undefined";
  • RowObject{ [prop: string]: unknown }(L12-L14),可携带任意属性,甚至可定义__children数组支持嵌套行;
  • CellChange = [number, string | number | ColumnDataGetterSetterFunction, CellValue, CellValue](L47):元组中的第二项是prop——可能是属性名、列索引,或在columns[].data为函数时是那个 getter/setter 函数本身;
  • ChangeSource的完整联合(L52-L58)包含'auto''edit''loadData''updateData''populateFromArray''spliceCol''spliceRow''timeValidate''dateValidate''validateCells',以及各插件来源如'Autofill.fill''ContextMenu.clearColumn''CopyPaste.paste''CopyPaste.cut''UndoRedo.redo''UndoRedo.undo''ColumnSummary.set''DataProvider.revert'等;
  • SourceRowData = RowObject | CellValue[](L31):一行源数据可以是值数组,也可以是键值对象;
  • ColumnDataGetterSetterFunction(L37-L40)是重载函数类型:单参(仅row)用于读取,双参(row, value)用于写入。

实例类型(Instance type)

类型说明
HotInstancenew Handsontable()返回的 Handsontable 实例。用于标注 ref 和接受活动实例的参数。
import Handsontable from 'handsontable'; import type { HotInstance } from 'handsontable'; let hot: HotInstance; hot = new Handsontable(document.querySelector('#grid')!, { data: myData, licenseKey: 'non-commercial-and-evaluation', });

HotInstance接口定义在 handsontable/src/core/types.ts,它描述了实例的完整 API 面:包括addHook/removeHook/runHooks等 hook 方法(其中addHook等方法对已知 hook 名会按Events[K]精确推导回调签名,对未知字符串则回退到宽松的HookCallback)、getSettings()/updateSettings()getSelectedRange()等选区方法,以及更多实例方法。HotInstanceRangeType都从 handsontable/src/index.ts 对外导出。

几何类(Geometry classes)

CellCoordsCellRange是运行时类(非仅类型),同时也可用作类型注解:

导出种类说明
CellCoords一个{ row, col }坐标对。由选择与导航 API 返回。
CellRange一个{ from: CellCoords, to: CellCoords }范围。由getSelectedRange()返回。
IndexMapper行或列的索引转换器。类型化访问hot.rowIndexMapperhot.columnIndexMapper
RangeType类型一个纯对象范围描述符,用于不需要CellRange实例的 API。
import { CellRange } from 'handsontable'; function logSelection(ranges: CellRange[]): void { for (const range of ranges) { console.log(`from [${range.from.row}, ${range.from.col}] to [${range.to.row}, ${range.to.col}]`); } }

CellCoordsCellRange从 handsontable/src/base.ts 重新导出(见 handsontable/src/index.ts),IndexMapper来自翻译层./translations(L212)。注意CellRange是值(value),所以上面的import { CellRange }会保留运行时导入,这点与import type的纯类型导入不同。

单元格函数注册表类型(Cell function registry types)

用这些字符串联合类型把typerenderereditorvalidator设置约束为已注册的名称

类型说明
CellType所有已注册单元格类型别名的联合:'text' \| 'numeric' \| 'checkbox' \| ...
EditorType所有已注册编辑器别名的联合。
RendererType所有已注册渲染器别名的联合。
ValidatorType所有已注册验证器别名的联合。
import type { ColumnSettings, CellType } from 'handsontable'; function buildColumn(type: CellType): ColumnSettings { return { type }; }

这些联合类型是由各注册表模块的类型别名typeof推导出来的:CellType定义于 handsontable/src/cellTypes/registry.ts(typeof AUTOCOMPLETE_TYPE | typeof CHECKBOX_TYPE | typeof DATE_TYPE | ...),EditorType于 handsontable/src/editors/index.ts,RendererType于 handsontable/src/renderers/index.ts。因此,当你通过registerCellType()/registerEditor()/registerRenderer()/registerValidator()注册自定义项时,联合类型会随注册表声明同步演进——这是"注册表驱动"类型设计的体现。

编辑器基类类型(Editor base type)

类型说明
BaseEditorInstanceBaseEditor类的类型。用于在自定义编辑器代码中标注参数和返回值。

Hooks 注册表类型(Hooks registry type)

类型说明
HooksRegistry描述静态的Handsontable.hooks对象(如.getRegistered())。在把 hooks 注册表作为类型化参数传递时很有用。
import type { HooksRegistry } from 'handsontable'; function listHooks(hooks: HooksRegistry): string[] { return hooks.getRegistered(); }

HooksRegistry从 handsontable/src/base.ts 导出(见 handsontable/src/index.ts)。

主题类型(Theme types)

这些类型在使用主题 API(themeName选项)或构建自定义主题时相关:

类型说明
ThemeConfigregisterTheme()的完整配置对象。
ThemeParams创建主题实例的构造参数。
ThemeBuilder主题工厂返回的 builder 对象。
ThemeColorScheme'light' \| 'dark' \| 'auto'
ThemeColorsConfig主题配置内的颜色 token 覆盖。
ThemeDensityConfig行高与单元格内边距 token 覆盖。
ThemeDensitySizes密度配置项内的数值尺寸。
ThemeIconsConfig图标 token 覆盖。
ThemeLightDarkValue带独立亮/暗变体的 token 值。
ThemeSizingConfig字体与间距 token 覆盖。
ThemeTokenValue单个设计 token 值。
ThemeTokensConfigThemeConfig内部使用的完整 token 映射。
BaseTheme所有内置主题继承的基础主题对象。

仓库中主题系统的实现位于 handsontable/src/themes 目录,内置主题的 CSS 产物通过build:themes-css/build:themes-umd等脚本构建(见 handsontable/package.json)。

Overlay 类型(Overlay type)

类型说明
OverlayType所有 Walkontable overlay 标识符的字符串联合:'top' \| 'bottom' \| 'left' \| 'right' \| ...。在高级渲染定制中很有用。

OverlayType的精确定义在 handsontable/src/3rdparty/walkontable/src/types.ts:'inline_start' | 'top' | 'top_inline_start_corner' | 'bottom' | 'bottom_inline_start_corner' | 'master'。可以看到它采用逻辑/物理方向混合命名(inline_startmaster),实际取值以源码为准。

公共 API 的隐式类型化(Implicit typing)

上面列出的类型是你在需要显式导入时才用的——即当你需要为自己的变量、参数或返回值标注类型时。

公共 API 的其余部分——插件、编辑器、渲染器、验证器、单元格类型,以及Handsontable类本身——都自带 TypeScript 声明。导入值的同时就会自动获得完整的类型安全和 IDE 自动补全,无需额外导入类型:

// 无需类型导入 -- ContextMenu 已完全类型化。 import { ContextMenu } from 'handsontable/plugins'; // 无需类型导入 -- AutocompleteEditor 已完全类型化。 import { AutocompleteEditor } from 'handsontable/editors'; // 无需类型导入 -- numericRenderer 已完全类型化。 import { numericRenderer } from 'handsontable/renderers'; // 无需类型导入 -- Handsontable 类及其实例方法已完全类型化。 import Handsontable from 'handsontable'; const hot = new Handsontable(container, { licenseKey: 'non-commercial-and-evaluation' }); hot.loadData(myData); // IDE 自动补全和类型检查在这里生效。

上文类型参考中的显式import type { ... }模式,只在"值不在当前作用域内、需要把类型单独用作注解"时才必要。仓库的exports映射也为子路径提供了声明:./plugins/*/index.*./editors/*/index.*./renderers/*/index.*./cellTypes/*/index.*等都会随构建产物一起发布(见 handsontable/package.json)。

值得补充的是,这些声明的正确性由专门的类型测试(type tests)守护:仓库在handsontable/src下有 69 个*.types.ts文件(如 handsontable/src/tests/public-types.types.ts),配合test:types脚本(handsontable/package.json)在 CI 中编译验证——它们不产生运行时行为,而是确保每个公开类型在真实使用场景下能通过类型检查。例如 handsontable/src/core/settings.ts 中提到的_strippedWidthTyped_columnNamedOptionsTyped_hotColumnGetValueFn等类型测试,就专门守护RemoveIndexSignatureColumnSettings的类型行为。

常见模式(Common patterns)

类型化设置对象(Type the settings object)

直接给 settings 标注类型,让 TypeScript 校验每一个选项:

import type { GridSettings } from 'handsontable'; const settings: GridSettings = { data: employees, colHeaders: ['Name', 'Department', 'Hire date'], columns: [ { data: 'name' }, { data: 'department' }, { data: 'hireDate', type: 'date', dateFormat: { year: 'numeric', month: '2-digit', day: '2-digit' } }, ], licenseKey: 'non-commercial-and-evaluation', };

使用Events类型化 hook 回调

Events把每个 hook 名映射到其回调签名。用Events[hookName]提取特定回调类型:

import type { Events, CellChange, ChangeSource } from 'handsontable'; const onAfterChange: Events['afterChange'] = (changes: CellChange[] | null, source: ChangeSource) => { if (!changes) return; console.log(source, changes); }; hot.addHook('afterChange', onAfterChange);

得益于HotInstance.addHook的重载设计(见 handsontable/src/core/types.ts),当 hook 名是已知的keyof Events时,回调参数会被精确推导;即使不手动标注,addHook('afterChange', cb)也能获得参数补全。

类型化自定义渲染器(Type a custom renderer)

自定义渲染器接收类型化的CellProperties参数:

import Handsontable from 'handsontable'; import type { CellProperties, HotInstance, GridSettings } from 'handsontable'; function statusRenderer( hotInstance: HotInstance, TD: HTMLTableCellElement, row: number, col: number, prop: string | number, value: string, cellProperties: CellProperties ): void { Handsontable.renderers.TextRenderer(hotInstance, TD, row, col, prop, value, cellProperties); TD.style.color = value === 'Active' ? 'green' : 'red'; }

注意CellPropertiesCellMeta多出渲染期字段(rowcolinstancevisualRowvisualColprop,见 handsontable/src/settings.ts),这正是渲染器签名里第七个参数的类型。更完整的自定义渲染器写法可参考单元格渲染器指南。

类型化自定义编辑器(Type a custom editor)

继承BaseEditorInstance(实际使用中常直接继承某个内置编辑器类),获得完整的 IDE 支持:

import { TextEditor } from 'handsontable/editors'; import type { GridSettings } from 'handsontable'; class RatingEditor extends TextEditor { override getValue(): string { return this.TEXTAREA?.value ?? ''; } override setValue(newValue: string): void { if (this.TEXTAREA) { this.TEXTAREA.value = newValue; } } } const settings: GridSettings = { columns: [{ editor: RatingEditor as unknown as string }], licenseKey: 'non-commercial-and-evaluation', };

TextEditor等编辑器类从handsontable/editors导入,自带完整类型(见 handsontable/src/editors/index.ts 处的导出)。关于自定义编辑器的完整流程,可参考单元格编辑器指南。

在 React ref 中类型化 HOT 实例

HotTable暴露的 ref 类型是HotTableRef,而非HotInstance——需要通过hotRef.current?.hotInstance读取 Handsontable 实例。wrapper 把网格选项作为独立 props 接收,所以没有settingsprop——把可复用的设置对象类型化为HotTableProps,再展开到<HotTable>上。更多实例 ref 的用法见 React methods。

import { useRef } from 'react'; import { HotTable } from '@handsontable/react-wrapper'; import type { HotTableRef, HotTableProps } from '@handsontable/react-wrapper'; export function Grid() { const hotRef = useRef<HotTableRef>(null); const settings: HotTableProps = { data: myData, licenseKey: 'non-commercial-and-evaluation', }; const selectFirstCell = () => { hotRef.current?.hotInstance?.selectCell(0, 0); }; return ( <HotTable ref={hotRef} {...settings} /> ); }

在 Angular 中类型化 HOT 实例

import { Component, ViewChild } from '@angular/core'; import { HotTableComponent, HotTableModule, GridSettings } from '@handsontable/angular-wrapper'; @Component({ standalone: true, imports: [HotTableModule], template: `<hot-table [settings]="gridSettings" />`, }) export class GridComponent { @ViewChild(HotTableComponent) hotTable!: HotTableComponent; readonly gridSettings: GridSettings = { data: myData, licenseKey: 'non-commercial-and-evaluation', }; }

通过hotTable.hotInstance读取 Handsontable 实例。

在 Vue ref 中类型化 HOT 实例

<script setup lang="ts"> import { ref } from 'vue'; import { HotTable } from '@handsontable/vue3'; import type { GridSettings } from 'handsontable'; const hotRef = ref<InstanceType<typeof HotTable> | null>(null); const settings: GridSettings = { data: myData, licenseKey: 'non-commercial-and-evaluation', }; </script> <template> <HotTable ref="hotRef" :settings="settings" /> </template>

ref 被类型化为HotTable组件类型;通过hotRef.value?.hotInstance读取HotInstance

小结与延伸阅读

Handsontable 的类型体系可以概括为三层:入口层handsontable/handsontable/base两个入口提供相同类型)、显式类型层(本文类型参考中列出的配置、数据、实例、几何、注册表、主题、Overlay 类型,需要时才import type)、隐式类型层(插件、编辑器、渲染器等值导入即带类型)。声明文件由最新 TypeScript 编译后降级到 TS 5.1 兼容,并由 69 个*.types.ts类型测试持续守护。

继续深入可参考:

  • 模块化构建(Modules) —— 了解handsontable/base入口对应的按需注册模块机制
  • 自定义插件(Custom plugins)
  • 单元格编辑器(Cell editor)
  • 单元格渲染器(Cell renderer)
  • 类型声明的核心源码:handsontable/src/settings.ts、handsontable/src/core/settings.ts、handsontable/src/core/types.ts、handsontable/src/index.ts
  • 声明降级机制:handsontable/scripts/downlevel-dts.mjs
  • 类型测试示例:handsontable/src/tests/public-types.types.ts
  • 前端
  • UI组件

【免费下载链接】handsontable

JavaScript Data Grid / Data Table with a Spreadsheet Look & Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡

项目地址:https://gitcode.com/gh_mirrors/ha/handsontable
点击查看免费下载
上一篇:Newtonsoft.Json 序列化与反序列化完全指南
下一篇:如何完整备份微信聊天记录:WeChatMsg三步导出终极指南

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

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

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

立即咨询