拓冰建站拓冰建站
首页 / 资讯中心 / 正文

TanStack Table 的 CreatedFilterFn 接口:理解 constructFilterFn 返回类型与自定义列过滤函数构建机制

前端UI组件【免费下载链接】table Headless UI for building powerful tables datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址https://gitcode.com/gh_mirrors/ta/table点击查看免费下载导读CreatedFilterFn是 TanStack Table本仓库为 headless UI 表格库的 TypeScript/JavaScript 实现覆盖 React、Vue、Solid、Svelte、Angular、Preact、Lit、Vanilla 等框架中列过滤系统的一个核心类型接口它是工厂函数constructFilterFn的返回值类型——一个既可直接作为FilterFn调用、又挂载着完整过滤定义filter比较器与各解析器的可调用函数对象。通过它你可以基于内置过滤函数用「展开 覆盖」的方式快速派生大小写不敏感、忽略变音符号、日期/数值区间等自定义过滤变体。读完本文你将掌握CreatedFilterFn的完整类型签名、与FilterFn/FilterFnDef的继承关系、底层constructFilterFn的实现原理以及如何结合resolveFilterValue/resolveDataValue/autoRemove/addMeta四要素写出可复用、可测试的自定义过滤函数。接口完整定义见 CreatedFilterFn.md源码位置在 columnFilteringFeature.types.ts。一、接口定位CreatedFilterFn 在列过滤体系中的角色在 TanStack Table 的列过滤特性columnFilteringFeature中存在三个相互关联但职责不同的类型类型用途源码位置FilterFnTFeatures, TData可直接挂到columnDef.filterFn或tableFeatures的filterFns注册表中的完整过滤函数接受row内部自行取值columnFilteringFeature.types.tsFilterFnDefTFeatures, TDataconstructFilterFn接受的定义对象值级比较器filter 可选解析器columnFilteringFeature.types.tsCreatedFilterFnTFeatures, TDataconstructFilterFn的返回类型既是可调用函数行为同FilterFn又带有FilterFnDef的全部属性columnFilteringFeature.types.ts接口文档原文对它的定位是The shape returned byconstructFilterFn: aFilterFnwith its definition attached, so variants can be built by spreading it into anotherconstructFilterFncall.constructFilterFn返回的形状一个携带自身定义的FilterFn因此可以通过把它展开到另一次constructFilterFn调用来构建变体。这揭示了CreatedFilterFn的本质——它把「定义」数据与「执行」函数合二为一一次调用constructFilterFn(def)之后返回的函数既能在行模型过滤管线中被调用又能通过filterFn.filter、filterFn.autoRemove、filterFn.resolveFilterValue、filterFn.resolveDataValue这些属性被读取或覆盖从而支持「基于已有过滤器派生新过滤器」的组合模式。二、类型签名逐字段剖析CreatedFilterFn的完整定义如下与文档 CreatedFilterFn.md 一致export interface CreatedFilterFn in out TFeatures extends TableFeatures, in out TData extends RowData, extends FilterFnDefTFeatures, TData { TRowFeatures extends TableFeatures, TRowData extends RowData( row: RowTRowFeatures, TRowData, columnId: string, filterValue: any, addMeta?: (meta: ExtractFilterMetaTRowFeatures) void, ): boolean }2.1 双组泛型参数与in out变型接口本身有两个泛型参数与父接口FilterFnDef共用TFeatures extends TableFeatures当前特性集feature set定义时所用。源码注释指出调用签名是独立的、更宽松的泛型TRowFeatures/TRowData因此一个创建好的过滤函数可以用于任何表的行无论它当初是针对哪个特性集定义的——这保证了过滤函数可以在不同特性组合的表格间复用。TData extends RowData行数据类型约束为表格行数据基类。值得注意in out变型注解variance annotationTFeatures与TData同时以输入in出现在函数参数位置和输出out出现在返回值/属性位置两种方式参与类型计算声明为不变invariant可避免类型系统在特性集推断时产生意外收窄。2.2 调用签名Call Signature与参数CreatedFilterFn本身是可调用类型调用签名参数如下参数类型说明rowRowTRowFeatures, TRowData当前正在被测试的行过滤函数可通过row.getValue(columnId)读取该列的值columnIdstring要过滤的列标识filterValueany过滤值。注意当表格管线调用时该值已由resolveFilterValue归一化见下文直接调用时需自行归一化addMeta?(meta) void可选回调用于把过滤元数据filter meta附着到行上见第五章返回值固定为boolean——true表示该行通过此过滤false表示被过滤掉。三、从 FilterFnDef 继承的四个属性CreatedFilterFn extends FilterFnDefTFeatures, TData因此它拥有FilterFnDef的全部属性。文档的 Properties 小节完整列出了这四个成员其语义如下源码注释见 columnFilteringFeature.types.ts3.1filter必选值级比较器filter: ( dataValue: any, filterValue: any, row: RowTFeatures, TData, columnId: string, addMeta?: (meta: ExtractFilterMetaTFeatures) void, ) boolean与FilterFn不同FilterFnDef.filter是值级比较器它接收的是「行的已解析数据值」和「已解析的过滤值」而不是整行。归一化逻辑被隔离到resolveDataValue/resolveFilterValue中。这样设计的好处是派生一个过滤函数变体时只需替换解析器无需重新实现比较逻辑。3.2resolveFilterValue?过滤值归一化每过滤器仅一次resolveFilterValue?: (filterValue: any) any对过滤值做归一化。表格对每个过滤器只应用一次resolveFilterValue而非每行一次并把解析后的值作为filterValue传给过滤函数。这在createFilteredRowModel中有明确实现createFilteredRowModel.tsresolvedColumnFilters.push({ id: columnFilter.id, filterFn, resolvedValue: filterFn.resolveFilterValue?.(columnFilter.value) ?? columnFilter.value, })对全局过滤同样如此createFilteredRowModel.ts。正因如此在表格外部直接调用过滤函数时需要先手动调用resolveFilterValue——constructFilterFn源码注释与单元测试都明确了这一约定filterFns.ts、filterFns.test.ts。3.3resolveDataValue?行数据值归一化每行一次resolveDataValue?: TransformDataValueFn对每一行的列值做归一化其类型TransformDataValueFn定义为(dataValue: any) any见 type-utils.ts。文档与源码均强调只有通过constructFilterFn构建的过滤函数包括全部内置过滤函数才会被行模型管线执行resolveDataValue。3.4autoRemove?自动移除规则autoRemove?: (filterValue: any) boolean当过滤值满足该规则时例如文本过滤器收到空字符串表格会把这条过滤从state.columnFilters中移除。源码注释还给出一个关键细节columnFilteringFeature.types.ts一旦提供了autoRemove它的判定就是权威的——被它保留的值即使为空字符串也会留在过滤状态中默认启发式会移除空字符串而undefined过滤值无论如何都会清空过滤器。setFilterValue的文档注释也印证了这一点「满足过滤器autoRemove规则的值会从过滤状态中移除」columnFilteringFeature.types.ts。四、底层原理constructFilterFn 如何构建 CreatedFilterFn接口的实现位于 filterFns.ts。constructFilterFn接受一个FilterFnDef返回CreatedFilterFnexport function constructFilterFn TFeatures extends TableFeatures any, TData extends RowData any, (def: FilterFnDefTFeatures, TData): CreatedFilterFnTFeatures, TData { const filterFn: CreatedFilterFnTFeatures, TData Object.assign( TRowFeatures extends TableFeatures, TRowData extends RowData( row: RowTRowFeatures, TRowData, columnId: string, filterValue: any, addMeta?: (meta: ExtractFilterMetaTRowFeatures) void, ): boolean { const rawValue row.getValue(columnId) const dataValue filterFn.resolveDataValue ? filterFn.resolveDataValue(rawValue) : rawValue // The defs comparator is typed against the factorys TFeatures/TData; // the callers row generics are erased at this boundary. return filterFn.filter( dataValue, filterValue, row as any, columnId, addMeta as any, ) }, def, ) return filterFn }关键实现细节Object.assign(fn, def)把定义对象的所有属性filter、autoRemove、resolveFilterValue、resolveDataValue直接粘贴到返回的函数上。这正是CreatedFilterFn「函数即定义」的结构来源。取值与解析链路调用时先row.getValue(columnId)取出原始值若resolveDataValue存在则先归一化再把(dataValue, filterValue, row, columnId, addMeta)交给def.filter比较器执行。泛型边界擦除工厂的TFeatures/TData与调用者的TRowFeatures/TRowData在运行时被擦除源码注释明确说明了这一类型边界the callers row generics are erased at this boundary。后期可变性因为属性挂在函数对象上resolveDataValue等属性可以在创建之后再赋值。测试用例honors resolveDataValue assigned after creationfilterFns.test.ts专门验证了这一点——创建后给upperCaseEquals.resolveDataValue赋一个toUpperCase转换即可让原本区分大小写的过滤器变得不区分大小写。4.1 全部内置过滤函数都是 CreatedFilterFn从源码结构看filterFns.ts 中全部 22 个内置过滤器均为constructFilterFn的产物可归为五类类别内置过滤器基础相等filterFn_equals、filterFn_weakEquals字符串filterFn_includesStringSensitive、filterFn_includesString、filterFn_equalsString、filterFn_equalsStringSensitive、filterFn_startsWith、filterFn_endsWith空值filterFn_empty、filterFn_notEmpty数值比较filterFn_greaterThan、filterFn_greaterThanOrEqualTo、filterFn_lessThan、filterFn_lessThanOrEqualTo区间filterFn_between、filterFn_betweenInclusive、filterFn_inNumberRange、filterFn_inDateRange数组filterFn_arrHas、filterFn_arrIncludes、filterFn_arrIncludesAll、filterFn_arrIncludesSome以字符串过滤器为例可见三个属性的协作模式filterFns.tsexport const filterFn_includesString constructFilterFn({ filter: (dataValue, filterValue) Boolean(dataValue?.includes(filterValue)), autoRemove: (val: any) testFalsy(val), resolveFilterValue: (val: any) String(val).toLowerCase(), resolveDataValue: (val: any) val null ? undefined : String(val).toLowerCase(), })比较器只关心「是否包含」大小写归一化被拆到两个 resolver 中——resolveFilterValue处理过滤值每过滤器一次resolveDataValue处理行值每行一次。这也是为何filterFn_includesString能同时匹配JoH、123等输入测试用例验证了resolveFilterValue(JoH) joh、resolveFilterValue(123) 123filterFns.test.ts。五、addMeta把过滤元数据附着到行上CreatedFilterFn调用签名中的可选参数addMeta?: (meta) void用于为行附加过滤元数据。行模型管线在调用过滤函数时会传入一个回调createFilteredRowModel.tsrow.columnFilters[id] currentColumnFilter.filterFn( row, id, currentColumnFilter.resolvedValue, (filterMeta) { if (!row.columnFiltersMeta) { row.columnFiltersMeta makeObjectMap() } row.columnFiltersMeta[id] filterMeta }, )过滤函数内部一旦调用addMeta(meta)该 meta 会被写入row.columnFiltersMeta[id]同时每个行都会维护row.columnFilters按列 ID 记录通过/失败状态与row.columnFiltersMeta记录可选元数据两个映射类型定义见 columnFilteringFeature.types.ts。meta 的类型通过ExtractFilterMetaTFeatures推导columnFilteringFeature.types.ts若特性集在tableFeatures({ ..., filterMeta: {} as MyFilterMeta })中声明了filterMeta类型槽位则该类型胜出否则回退到全局声明合并的FilterMeta接口默认空接口见 columnFilteringFeature.types.ts。TableFeatures类型文档对filterMeta槽位的说明TableFeatures.ts该槽位是纯类型type-only传入的幻影值phantom value在运行时会被剥离仅用于类型推断。六、实战基于 CreatedFilterFn 派生自定义过滤函数CreatedFilterFn最核心的工程价值是可以借助「展开内置过滤器 覆盖解析器」快速派生变体。constructFilterFn源码注释filterFns.ts与单元测试filterFns.test.ts共同给出了权威示例——忽略变音符号的包含匹配import { constructFilterFn, filterFn_includesString, } from ./filterFns const normalize (value: unknown) String(value ?? ) .toLowerCase() .normalize(NFD) .replace(/\p{Diacritic}/gu, ) const includesStringIgnoreDiacritics constructFilterFn({ ...filterFn_includesString, // 继承 filter、autoRemove、原有 resolver resolveFilterValue: normalize, // 只覆盖过滤值归一化 resolveDataValue: normalize, // 只覆盖行值归一化 })filterFn_includesString本身就是CreatedFilterFn因此被展开后其filter比较器与autoRemove规则原样继承变体只需更换两个 resolver。测试用例验证了它的完整行为filterFns.test.ts行值Éric Bernard配合过滤值eric→ 命中true而基类filterFn_includesString无法跨变音符号匹配false行值Zoe Smith配合过滤值Zoë→ 命中通过展开继承了基类的autoRemoveautoRemove() true、autoRemove(x) false接入完整行模型管线后columnFilters: [{ id: name, value: ERIC }]能把[Éric Bernard, Eric Brandon]从数据[Enrico Toccacelo, Éric Bernard, Eric Brandon, null]中过滤出来。6.1 注册与接入表格自定义过滤函数有两种接入方式说明见 createFilteredRowModel.ts 与 TableFeatures.ts直接挂在列上columns: [{ accessorKey: name, id: name, filterFn: includesStringIgnoreDiacritics }]——这种用法无需任何注册注册进filterFns槽位tableFeatures({ columnFilteringFeature, filteredRowModel: createFilteredRowModel(), filterFns: { includesString: filterFn_includesString, myCustomFilterFn } })——注册后可按名称引用并且ExtractFilterFnKeys类型系统会把注册表键名约束为唯一合法名称columnFilteringFeature.types.ts。注意不要整体导入并注册导出的filterFns注册表对象——源码注释明确提示这会关闭 tree-shaking把所有内置过滤函数打进产物filterFns.ts。应只导入实际用到的单个filterFn_*函数。另外ExtractFilterFnKeys的解析规则是当特性集声明了filterFns注册表时其键是唯一合法名称否则回退到全局声明合并的FilterFns接口与内置名称BuiltInFilterFn。6.2 直接调用约定在表格管线之外直接调用CreatedFilterFn时须遵守「先归一化过滤值」的约定filterFns.tsfn(row, columnId, fn.resolveFilterValue?.(value) ?? value)否则resolveFilterValue的归一化如小写化不会生效过滤行为将与表格内不一致。autoRemove同样可在函数上直接读取用于 UI 侧判断是否清除过滤条件。七、设计要点与最佳实践小结值级比较与归一化解耦CreatedFilterFn通过把「比较」filter与「归一化」resolveFilterValue/resolveDataValue分离使自定义过滤函数的开发变成「只换解析器、不动比较器」的组合式开发resolveDataValue仅对constructFilterFn构建的函数生效。性能特征resolveFilterValue每过滤器执行一次行模型构建时见 createFilteredRowModel.tsresolveDataValue每行执行一次——把代价高的归一化放到resolveFilterValue侧可获得明显收益过滤行模型本身由tableMemo记忆化依赖preFilteredRowModel、columnFilters、globalFilter三个原子createFilteredRowModel.ts。组合优于复制用「展开CreatedFilterFn 覆盖」而非从零编写FilterFnDef可同时继承filter、autoRemove与已有 resolver减少出错面创建后仍可给函数属性赋值来追加归一化逻辑。autoRemove是权威规则提供autoRemove后被它保留的空值会留在过滤状态undefined值始终清除过滤器。实现范围类过滤器的autoRemove时注意用Array.isArray防御标量值误判between、betweenInclusive、inNumberRange的回归测试均覆盖了此场景见 filterFns.test.ts。善用 meta需要向 UI 层传递过滤附加信息如匹配的命中位置、匹配方式时通过addMeta写入row.columnFiltersMeta并在特性集中声明filterMeta类型槽位以获得完整类型推断。延伸阅读接口参考CreatedFilterFn、FilterFnDef、TableFeatures、RowData、Row源码实现filterFns.tsconstructFilterFn与全部内置过滤器、columnFilteringFeature.types.ts全部过滤相关类型、createFilteredRowModel.ts过滤行模型管线单元测试filterFns.test.ts覆盖constructFilterFn、内置过滤器、autoRemove边界与行模型集成使用指南列过滤相关文档、列定义中的 filterFn赞分享前端UI组件【免费下载链接】table Headless UI for building powerful tables datagrids for TS/JS - React-Table, Vue-Table, Solid-Table, Svelte-Table项目地址https://gitcode.com/gh_mirrors/ta/table点击查看免费下载相关推荐深入解析 Svelte-Table 的 CreateTableHookResult基于 TanStack Table 的自定义表格 Hook 返回接口深入解析 Svelte Table 的 CreateTableHookResult基于 TanStack Table 的自定义表格 Hook 返回接口 导读前端UI组件TanStack Alpine Table 列过滤实战指南从内置过滤函数到自定义过滤与手工服务端过滤TanStack Alpine Table 列过滤实战指南从内置过滤函数到自定义过滤与手工服务端过滤 本指南面向使用 tanstack/alpine tab前端UI组件TanStack Table constructFilterFn 深度解析从值级比较器到可复用过滤函数的工厂函数TanStack Table constructFilterFn 深度解析从值级比较器到可复用过滤函数的工厂函数 导读 constructFilterFn前端UI组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门