发现于 #5015 的实施(为重写 README 的 TypeScript 示例,对 DashboardWidgetSchema 逐键做了编译探针,顺带量到这条)。Filed unassigned, not claiming。观察级 —— 今天没有用户会撞上:as any 只是让一个已经合法的读取绕过类型系统,行为与收窄后完全一致,所以打 finding、不打 pm:queue,定级交分诊。
现象
packages/plugin-dashboard/src/DashboardRenderer.tsx:524-528:
// ADR-0021 — a widget bound to a semantic-layer dataset renders through
// the governed queryDataset path (DatasetWidget) instead of the inline
// object-aggregate schema. `as any` because the bundled DashboardWidget
// type gains `dataset` only after objectui bumps @objectstack/spec.
const datasetBound = !!(widget as any).dataset;
注释给出的理由是「dataset 要等 objectui 把 @objectstack/spec 升上去才会出现在类型上」。这个条件已经满足:仓里装的是 @objectstack/spec@17.0.0,其 DashboardWidgetSchema 已声明 dataset 与 values,而 packages/types/src/complex.ts:676 的 DashboardWidgetSchema extends Omit< Partial< SpecDashboardWidget >, 'type' | 'options' | 'chartConfig' | 'filter' > 把它们直接继承进来 —— dataset 不在 Omit 名单里。
对构建产物 packages/types/dist/index.d.ts 做 strict 编译,下面这段零诊断:
import type { DashboardWidgetSchema } from '@object-ui/types';
export const boundKpi: DashboardWidgetSchema = {
id: 'kpi_invoices',
type: 'metric',
title: 'Invoices',
dataset: 'invoices',
values: ['count'],
colorVariant: 'success',
};
也就是说 widget.dataset 现在直接读就能通过类型检查,as any 是纯粹的多余收缩 —— 而且是有害的多余:as any 之后 .dataset 的类型是 any,若将来 spec 把 dataset 的形状改掉(比如从 string 变成对象),这一处不会红。
影响面
同文件里 as any 读 widget 键的还有 :539 的 (widget as any).data。那一处不同:data 确实不在 DashboardWidgetSchema 上(spec 与 objectui 的覆写都没声明它),所以它是「读一个未声明的键」,不是过期的 cast —— 若真要收窄,得先裁 data 该不该进契约,与本单不是一件事。本单只主张 :528 的 dataset 这一处及其注释。
未量:values 是否也有类似的 as any 读取点(本单只核了 dataset 的这一处)。
复核方式
sed -n '520,545p' packages/plugin-dashboard/src/DashboardRenderer.tsx
sed -n '676,680p' packages/types/src/complex.ts
grep -n "dataset: z.ZodString" node_modules/.pnpm/@objectstack+spec@*/node_modules/@objectstack/spec/dist/ui/index.d.ts
grep -rn "widget as any" packages/plugin-dashboard/src/DashboardRenderer.tsx
发现于 #5015 的实施(为重写 README 的 TypeScript 示例,对
DashboardWidgetSchema逐键做了编译探针,顺带量到这条)。Filed unassigned, not claiming。观察级 —— 今天没有用户会撞上:as any只是让一个已经合法的读取绕过类型系统,行为与收窄后完全一致,所以打finding、不打pm:queue,定级交分诊。现象
packages/plugin-dashboard/src/DashboardRenderer.tsx:524-528:注释给出的理由是「
dataset要等 objectui 把@objectstack/spec升上去才会出现在类型上」。这个条件已经满足:仓里装的是@objectstack/spec@17.0.0,其DashboardWidgetSchema已声明dataset与values,而packages/types/src/complex.ts:676的DashboardWidgetSchema extends Omit< Partial< SpecDashboardWidget >, 'type' | 'options' | 'chartConfig' | 'filter' >把它们直接继承进来 ——dataset不在Omit名单里。对构建产物
packages/types/dist/index.d.ts做strict编译,下面这段零诊断:也就是说
widget.dataset现在直接读就能通过类型检查,as any是纯粹的多余收缩 —— 而且是有害的多余:as any之后.dataset的类型是any,若将来 spec 把dataset的形状改掉(比如从string变成对象),这一处不会红。影响面
同文件里
as any读 widget 键的还有:539的(widget as any).data。那一处不同:data确实不在DashboardWidgetSchema上(spec 与 objectui 的覆写都没声明它),所以它是「读一个未声明的键」,不是过期的 cast —— 若真要收窄,得先裁data该不该进契约,与本单不是一件事。本单只主张:528的dataset这一处及其注释。未量:
values是否也有类似的as any读取点(本单只核了dataset的这一处)。复核方式