观察类发现(finding,不进 pm:queue),发现于 #3831 / PR #3908 的实施过程。运行时正确、今天没有用户会撞到 —— 是类型面与实际契约的漂移。
机制
packages/types/src/data.ts:53 声明:
/**
* Filter expression
* @example { age: { $gt: 18 }, status: 'active' }
*/
$filter?: Record< string, any >;
即只描述 MongoDB 风格的 field-keyed 记录形式。但仓内规范下沉口 packages/core/src/utils/filter-converter.ts 的 toFilterNode / mergeFilterNodes 返回的是 FilterNode | Record< string, any > | undefined —— AST 数组是其正常返回值之一 ,而两个既有消费者一直把它送进 $filter:
packages/plugin-list/src/ListView.tsx 的 buildEffectiveFilter(baseFilter: unknown, …),返回 any[] | Record< string, any > | undefined(喂表格与导出两条路);
packages/plugin-view/src/ObjectView.tsx(calendar / kanban / gallery / timeline)。
数据源侧完全支持:packages/data-objectstack/src/index.ts 的 translateFilterToAST 明确列出五种可接受输入,数组形式在列。所以运行时是对的,声明是窄的 。
有害在哪(只有类型面)
该类型不设防且方向错误 :Record< string, any > 会接受数组(数组满足 any 的字符串索引 —— tsc --strict 实测 exit 0),所以它既没挡住不该进的形状,也没描述该进的形状。record:related_list.add.picker.filter 全仓零读点:作者限定了 Add 选择器的候选范围,对话框照样提供该对象的全部记录 #3831 正是踩在这一点上:baseFilter?: Record< string, any > 接受了规则数组、随后被对象展开压成 {"0": {...}},类型全绿而查询去过滤名为 0 的列。
新写消费者的人读 $filter 的类型与 @example,会以为只有记录形式合法,于是可能给数组路径补一段"容错转换" —— 正是 AGENTS.md #0.1 反对的方向。
落地建议
把 $filter 声明成它实际接受的联合(记录形式 | AST 节点),@example 补一条数组示例,并在注释里指名 translateFilterToAST 为权威可接受集。属 packages/types 公共面,会波及若干消费者的类型标注,故不宜搭在功能 PR 上 —— PR #3908 只在单个赋值点(packages/fields/src/widgets/useRecordQuery.ts)就地转换并注明原因,没有顺手改这个共享类型。
参考位置
packages/types/src/data.ts:53(声明)
packages/core/src/utils/filter-converter.ts(toFilterNode / mergeFilterNodes 的返回类型)
packages/plugin-list/src/ListView.tsx(buildEffectiveFilter)/ packages/plugin-view/src/ObjectView.tsx(两个既送数组的生产者)
packages/data-objectstack/src/index.ts(translateFilterToAST,实际可接受集)
关联:#3831 / PR #3908 (发现来源)、#3948(两份 operator 词汇表漂移的先例)
观察类发现(
finding,不进pm:queue),发现于 #3831 / PR #3908 的实施过程。运行时正确、今天没有用户会撞到 —— 是类型面与实际契约的漂移。机制
packages/types/src/data.ts:53声明:即只描述 MongoDB 风格的 field-keyed 记录形式。但仓内规范下沉口
packages/core/src/utils/filter-converter.ts的toFilterNode/mergeFilterNodes返回的是FilterNode | Record< string, any > | undefined—— AST 数组是其正常返回值之一,而两个既有消费者一直把它送进$filter:packages/plugin-list/src/ListView.tsx的buildEffectiveFilter(baseFilter: unknown, …),返回any[] | Record< string, any > | undefined(喂表格与导出两条路);packages/plugin-view/src/ObjectView.tsx(calendar / kanban / gallery / timeline)。数据源侧完全支持:
packages/data-objectstack/src/index.ts的translateFilterToAST明确列出五种可接受输入,数组形式在列。所以运行时是对的,声明是窄的。有害在哪(只有类型面)
Record< string, any >会接受数组(数组满足any的字符串索引 ——tsc --strict实测 exit 0),所以它既没挡住不该进的形状,也没描述该进的形状。record:related_list.add.picker.filter全仓零读点:作者限定了 Add 选择器的候选范围,对话框照样提供该对象的全部记录 #3831 正是踩在这一点上:baseFilter?: Record< string, any >接受了规则数组、随后被对象展开压成{"0": {...}},类型全绿而查询去过滤名为0的列。$filter的类型与@example,会以为只有记录形式合法,于是可能给数组路径补一段"容错转换" —— 正是 AGENTS.md #0.1 反对的方向。落地建议
把
$filter声明成它实际接受的联合(记录形式 | AST 节点),@example补一条数组示例,并在注释里指名translateFilterToAST为权威可接受集。属packages/types公共面,会波及若干消费者的类型标注,故不宜搭在功能 PR 上 —— PR #3908 只在单个赋值点(packages/fields/src/widgets/useRecordQuery.ts)就地转换并注明原因,没有顺手改这个共享类型。参考位置
packages/types/src/data.ts:53(声明)packages/core/src/utils/filter-converter.ts(toFilterNode/mergeFilterNodes的返回类型)packages/plugin-list/src/ListView.tsx(buildEffectiveFilter)/packages/plugin-view/src/ObjectView.tsx(两个既送数组的生产者)packages/data-objectstack/src/index.ts(translateFilterToAST,实际可接受集)关联:#3831 / PR #3908(发现来源)、#3948(两份 operator 词汇表漂移的先例)