Skip to content

finding(types): ObjectViewSchema's table and form slots declare ZERO properties — the same Omit-under-index-signature collapse as #6151, in property position #6269

Description

@yinlianghui

⚠️ Generic parameters are written spaced (Omit< T, K >) throughout — GitHub's body
sanitizer eats a fragment that opens with an identifier in angle brackets, which is what
mangled #6151's first revision.

Measured while fixing #6151 (PR #6267). Filed unassigned, deliberately not fixed there:
the mechanism is identical but the correct replacement shape is a design question, not the
mechanical move #6151 took.

The fact

packages/types/src/objectql.ts declares two slots by deriving from a schema type:

  • line 1480 — table?: Partial< Omit< ObjectGridSchema, 'type' | 'objectName' > >
  • line 1486 — form?: Partial< Omit< ObjectFormSchema, 'type' | 'objectName' | 'mode' > >

Both derived types declare zero properties. Measured against the built dist through
the TypeScript checker:

GridOmit = Omit< ObjectGridSchema, 'type' | 'objectName' > -> 0 declared properties
FormOmit = Omit< ObjectFormSchema, 'type' | 'objectName' | 'mode' > -> 0 declared properties
ObjectGridSchema (the source type) -> 61 declared properties

61 to 0.

Why — the same mechanism as #6151

Omit< T, K > is Pick< T, Exclude< keyof T, K > >, and keyof T on a type carrying a
string index signature is string | number — the literal member names are absorbed. Both
ObjectGridSchema and ObjectFormSchema inherit BaseSchema's [key: string]: any
(#5155), so the Pick rebuilds a type holding the index signature and none of the named
members.

#6151 was this collapse in a heritage clause (interface StackSchema extends Omit< … >).
This is the same collapse in property position, which is why #6151's fix does not reach
it and why its new guard does not cover it: that guard walks the LayoutSchema union, and
these are properties on ObjectViewSchema, not union members.

What it costs

Partial< Omit< … > > of a collapsed type is a bare index signature, so the slot accepts
anything: table: { colunms: 3 } (typo), table: { pageSize: 'ten' }, table: 42's
object cousins — all type-check. And editor completion inside table: { … } offers nothing
at all, so an author writing a view has no discoverable surface for a slot documented as
"Inherits from ObjectGridSchema". The declaration promises inheritance and delivers any.

Same shape of harm as #6151, and the same reason it survived: nothing errors, because the
index signature answers every key.

Why this is not a mechanical fix

#6151 could lift the shared members into an index-signature-free interface because
FlexSchema and StackSchema are peers with an identical member set. These two are not
that shape:

  • The slots deliberately exclude identity keys (type, objectName, mode) — the
    intent is "the grid's configuration, minus what the view already fixes". Restoring that
    intent needs a decision about which keys are configuration, and ObjectGridSchema has
    61 of them.
  • Partial< … > on top means the fix must preserve "all optional" while restoring the
    member names.
  • Fixing it is a tightening on a published type: metadata that currently type-checks
    because everything is any may start erroring. That needs the same downstream sweep
    fix(types): StackSchema ships its declared members instead of collapsing under BaseSchema's index signature #6267 did, and possibly a ledger.

Options, none ruled here:

  1. A shared configuration interface free of BaseSchema, the finding(types): StackSchema, declared as Omit-of-FlexSchema-minus-type, erases every named member — it ships declaring only type, because Omit collapses under BaseSchema's index signature #6151 route — correct, but
    needs someone to partition ObjectGridSchema's 61 members into "identity" and
    "configuration".
  2. Pick with an explicit key list instead of OmitPick with literal keys never
    computes keyof T, so it does not collapse. Immediate and safe, but the key list is a
    duplicate to keep in sync.
  3. Sequence behind 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 (the phased zod .strict() ruling), which removes the index
    signature at the root and closes this class everywhere at once.

Scope

These two are the ones measured. The rest of the Omit population in packages/types/src
was swept in #6267 and is healthy: the three other heritage clauses (DashboardWidgetSchema,
ActionParam, NavigationArea) Omit over @objectstack/spec types that carry no index
signature, and data-protocol.ts:933's BaseValidation is fine for the same reason.
⚠️index.ts:900 has a generic & Partial< Omit< T, 'type' > > whose behaviour depends on
what T is instantiated with — not measured, and worth checking as part of any fix here.

Refs: #6151 (same mechanism, heritage clause, fixed in #6267) · #5155 (the index signature)
· #6143 (where the class first surfaced).

Metadata

Metadata

Assignees

Labels

domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions