You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
finding(types): BaseSchema's [key: string]: any leaves every component schema open, so a "declare the surface" fix can never reject a misspelled TOP-LEVEL key #5155
Found while implementing #5018 (ObjectMapSchema alignment). Filed unassigned, not claiming. Observation-class: nothing is broken today that a user hits directly — what is recorded is a structural ceiling on what "declared = enforced" can mean for component schemas.
/** * Additional properties specific to the component type. * This index signature allows type-specific extensions. */[key: string]: any;
Every component schema in the repo extends BaseSchema (ObjectGridSchema, ObjectFormSchema, ObjectMapSchema, …). An index signature disables TypeScript's excess-property check, so a misspelled top-level key type-checks on every one of them:
constschema: ObjectMapSchema={type: 'object-map',objectName: 'stores',enableClusterng: true,// accepted — no error, no hint};
This was measured, not inferred: #5018's first draft pinned the opposite with @ts-expect-error, and tsc answered TS2578: Unused '@ts-expect-error' directive on exactly those two pins. The pins were corrected to record the real boundary (packages/plugin-map/src/ObjectMap.schemaAlignment.test.tsx, the "does NOT reject a misspelled TOP-LEVEL key" case).
It is the exact hiding place the platform ranks as most expensive. Loose declaration plus tolerant consumption is where AI-authored metadata errors survive, and an index signature is the loosest possible declaration — it accepts a key the author invented, at the layer that is supposed to reject it.
The zod half does not cover for it either.packages/types/src/zod/objectql.zod.ts builds component schemas with BaseSchema.extend({...}), and those are not .strict() — so an unknown top-level key survives a safeParse too. Neither layer says anything.
What is NOT being claimed
That the index signature should simply be deleted. It is load-bearing for the schema-driven model — renderers do carry keys the shared base cannot enumerate, and the doc comment says so. The costs of the alternatives are real and want measuring before anything is ruled:
Keep it on BaseSchema, drop it from the leaf component interfaces (Omit-and-restate, or a Strict<T> helper applied at the leaves): closes authoring while leaving the base's extensibility story intact; costs a mechanical change at every leaf and a gate to keep new leaves in line.
Move the enforcement to the zod layer (.strict() on the component schemas, publish-time validation rejecting loudly): catches the AI-authored-metadata case where it actually lands, without touching the TS surface; does nothing for a TypeScript author's editor.
Triage decides whether this is worth a decision at all — it may well be correct to keep the index signature and stop implying that declared surfaces are closed. In that case the useful output is a documented statement of the ceiling, so cards stop being written as if declaring a key made its neighbours illegal.
Found while implementing #5018 (ObjectMapSchema alignment). Filed unassigned, not claiming. Observation-class: nothing is broken today that a user hits directly — what is recorded is a structural ceiling on what "declared = enforced" can mean for component schemas.
The fact
packages/types/src/base.tsclosesBaseSchemawith:Every component schema in the repo extends
BaseSchema(ObjectGridSchema,ObjectFormSchema,ObjectMapSchema, …). An index signature disables TypeScript's excess-property check, so a misspelled top-level key type-checks on every one of them:This was measured, not inferred: #5018's first draft pinned the opposite with
@ts-expect-error, andtscansweredTS2578: Unused '@ts-expect-error' directiveon exactly those two pins. The pins were corrected to record the real boundary (packages/plugin-map/src/ObjectMap.schemaAlignment.test.tsx, the "does NOT reject a misspelled TOP-LEVEL key" case).Why it is worth recording
It silently bounds a whole card class.
ObjectMapSchema(types)声明的键几乎与 ObjectMap 实际读的键无交集:map块整体未建模,组件 props 类型写的是ObjectGridSchema,每处 map 读取都走as any#5018 was ruled precisely to make a misspelling loud — "latitudeFieIdcurrently renders a silent empty map". The fix does achieve that, but only because themapBLOCK is a plain interface with no index signature. The same card's top-level half is unenforceable, and the next "declare what the renderer reads" card will discover this again from scratch unless it is written down. Thefindingcards already on this shape (finding(plugin-grid): ObjectGrid 在ObjectGridSchema.columns上容忍未声明的accessorKey/header拼写 —— 声明类型是 strict 的 ListColumn,#3104 的列身份门禁结构上看不见这条支路 #5068, finding(types):MobileOverrides.navigation发布了三值导航词表,全仓零读点 —— 与 #3985 同族的另一处「声明即全部」 #4919, finding(docs/types):content/docs/blocks/block-schema.mdx教的整族 block 词汇表(含节点位置的slot)没有任何 renderer #4895, [finding] Studio 的导航消费端读了六种 app nav schema 拒绝的拼写,外加一个不存在的view变体 #4881) are all about declaration-vs-consumption forks; this is the reason one whole direction of them cannot be closed by declaring.It is the exact hiding place the platform ranks as most expensive. Loose declaration plus tolerant consumption is where AI-authored metadata errors survive, and an index signature is the loosest possible declaration — it accepts a key the author invented, at the layer that is supposed to reject it.
The zod half does not cover for it either.
packages/types/src/zod/objectql.zod.tsbuilds component schemas withBaseSchema.extend({...}), and those are not.strict()— so an unknown top-level key survives asafeParsetoo. Neither layer says anything.What is NOT being claimed
That the index signature should simply be deleted. It is load-bearing for the schema-driven model — renderers do carry keys the shared base cannot enumerate, and the doc comment says so. The costs of the alternatives are real and want measuring before anything is ruled:
ObjectMapSchema(types)声明的键几乎与 ObjectMap 实际读的键无交集:map块整体未建模,组件 props 类型写的是ObjectGridSchema,每处 map 读取都走as any#5018 shape, repeated ~20 times), and would break any consumer passing through extra keys.BaseSchema, drop it from the leaf component interfaces (Omit-and-restate, or aStrict<T>helper applied at the leaves): closes authoring while leaving the base's extensibility story intact; costs a mechanical change at every leaf and a gate to keep new leaves in line..strict()on the component schemas, publish-time validation rejecting loudly): catches the AI-authored-metadata case where it actually lands, without touching the TS surface; does nothing for a TypeScript author's editor.Triage decides whether this is worth a decision at all — it may well be correct to keep the index signature and stop implying that declared surfaces are closed. In that case the useful output is a documented statement of the ceiling, so cards stop being written as if declaring a key made its neighbours illegal.
Provenance
ObjectMapSchema(types)声明的键几乎与 ObjectMap 实际读的键无交集:map块整体未建模,组件 props 类型写的是ObjectGridSchema,每处 map 读取都走as any#5018 (claude/issue-5018-objectmap-schema-alignment), TS version as pinned by the repo.ObjectGridSchema.columns上容忍未声明的accessorKey/header拼写 —— 声明类型是 strict 的 ListColumn,#3104 的列身份门禁结构上看不见这条支路 #5068 (grid columns accept an undeclared spelling at runtime), finding(types): the zod BaseSchema mirror still declares visible/disabled as boolean and ariaLabel as string, after the TS declarations widened #4605 (the zod BaseSchema mirror drifted from the TS one).