Skip to content

[finding][spec] FilterCondition 的索引签名让未知 $ 算子永远不是类型错误 —— 「$like 本可在类型层拦住」是实测不成立的 #6074

Description

@qq9340100

#5181IDataDriver 的 query 参数去掉冗余 object)落地过程中顺带实测出来的,与该单的修复无关,也不由该单的修复解决。单独记录,免得 cloud 车道按 #5181/#4860 的证据行去期待一个不会发生的效果。

实测

packages/spec/src/data/filter.zod.tsTS 类型 FilterCondition 是一个开放索引签名:

export type FilterCondition = {
  [key: string]:
    | any                                        // 隐式相等 key: value
    | z.infer< typeof FieldOperatorsSchema >     // 显式算子 key: { $op: value }
    | FilterCondition;                           // 嵌套关系
} & { $and?: ; $or?: ; $not?:  };

索引签名的值类型里有 any,所以任何键、任何值都合法。实测(packages/spec/src/contracts/data-driver.test.ts#5181 的 PR 里作为一条诚实性断言留下):

// 没有任何 cast,tsc 全绿
const unknownOperator: DriverQuery = { where: { name: { $like: 'acme%' } } };

pnpm --filter @objectstack/spec typecheck 对这一行零报错。

为什么值得记一笔

#5181 正文(转录自已关闭的 #4860)写着「cloud#1030 的 $like 本可在类型层拦住」。这一句不成立:cloud 侧那片 as any 确实关掉了一批类型检查(orderBySortNodefieldslimit$and/$or/$not 的结构),但没有关掉「where 里的未知 $ 算子」这一条 —— 因为它从来就没被打开过。#5181 删掉 cast 之后,$like 依然编译通过,依然只在运行时被校验层拒收。

这不是要推翻 #5181 的判断(它按「删掉冗余参数」自身的理由是成立的,且实测确实恢复了 orderBy/fields/limit 那几条),只是把它的作用面说准。

#5701 / #4706 的关系

#5701(已 closed/completed)走的是校验期词表:zod 层移除 $regex、新增 $icontains、未知算子响亮拒收。那条路线管的是运行时的门,结构上管不到 TS 类型这道门 —— FilterCondition 是手写 TS 类型,不是从 zod 推导出来的,两者各走各的。所以本单不是 #5701 的子项,也不 blocked-by 它。

权衡(不预设结论,留给分诊)

想把类型这道门也关上,就得让 FilterCondition 的索引签名从 any 收窄成「字段名 → 值 | 封闭算子对象」的联合。代价是真实的,且不小:

判据不明显,因此按 finding 记录、不挂 pm:queue,请分诊轮定级。

会话:session_011M7UwH25Unfi73UHim7ajY#5181 实施期间发现,未认领)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions