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): PartialSchema< T > declares ONE property for every instantiation — the #6269 collapse in a mapped-type alias, and it has zero consumers #6397
⚠️ 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 and #6269's first revisions.
Measured while fixing #6269, in the same pass that card explicitly asked for ("⚠️ 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").
Filed unassigned and deliberately not fixed in #6269's PR: the dispatch order for that
card ruled "measure it and report the number — do not widen this PR to fix it without
reporting first."
Every instantiation declares exactly one property. Measured through the TypeScript
checker against the emitted index.d.ts (the same instrument as #6269's 61 → 0 reading):
PartialSchema< ObjectGridSchema > -> 1 declared property: type (source: 61)
PartialSchema< ObjectFormSchema > -> 1 declared property: type (source: 67)
PartialSchema< ObjectViewSchema > -> 1 declared property: type (source: 27+)
PartialSchema< ButtonSchema > -> 1 declared property: type (source: 27)
All four also carry a live [key: string]: any, so the type accepts anything.
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. Every T extends BaseSchema inherits BaseSchema's [key: string]: any (#5155), so the Partial< Omit< T, 'type' > > half of the intersection rebuilds a type holding the index
signature and none of the named members. The explicit { type: T['type'] } half is the only
reason the count is 1 rather than 0.
This is the same collapse #6151 fixed in a heritage clause and #6269 fixed in property
position. Here it is in a generic mapped-type alias, which is why neither card's guard
sees it: #6151's walks the LayoutSchema union, #6269's reads ObjectViewSchema's two slot
properties.
What it costs today: nothing measurable — the type has no consumers
Grepped across packages/, apps/, examples/, content/ and docs/ (excluding node_modules): two occurrences of the identifier, and both are the declaration itself — packages/types/src/index.ts:905 (source) and packages/types/dist/index.d.ts:130 (its own
emitted copy). No call site, no re-export under another name, no documentation page, no test.
Its doc comment says "Useful for partial schema definitions in editors"; nothing in this
repo's editors uses it.
So the harm is not a live miscompile — it is a published, exported type that promises { type } & everything-else-optional and delivers { type } & any, available for the next
consumer to adopt and be silently unprotected by.
Options, none ruled here
Retire it. Zero in-repo consumers, and it is a convenience alias rather than a
capability — the startup-scope reading is that a declared-and-unconsumed surface does not
earn a repair. Cost: it is exported from @object-ui/types, so an out-of-tree consumer
could exist; needs the usual removal route (a minor under this repo's no-major
convention, or a deprecation window).
My reading is that 1 and 3 are the live pair, and they are compatible: retiring an
unconsumed alias is cheap now, and if it is kept instead, #5155 fixes it for free later. What
is not defensible is leaving it declared, published, collapsed, and unpinned.
Refs: #6269 (same mechanism, property position — this was measured in its PR) · #6151 (same
mechanism, heritage clause) · #5155 (the root index signature) · #6143 (where the class first
surfaced).
Omit< T, K >) throughout — GitHub's bodysanitizer eats a fragment that opens with an identifier in angle brackets, which is what
mangled #6151's and #6269's first revisions.
Measured while fixing #6269, in the same pass that card explicitly asked for ("⚠️
index.ts:900has a generic& Partial< Omit< T, 'type' > >whose behaviour depends on whatTis instantiated with — not measured, and worth checking as part of any fix here").Filed unassigned and deliberately not fixed in #6269's PR: the dispatch order for that
card ruled "measure it and report the number — do not widen this PR to fix it without
reporting first."
The fact
packages/types/src/index.ts:905:Every instantiation declares exactly one property. Measured through the TypeScript
checker against the emitted
index.d.ts(the same instrument as #6269's 61 → 0 reading):All four also carry a live
[key: string]: any, so the type accepts anything.Why — the #6269 / #6151 mechanism, third position
Omit< T, K >isPick< T, Exclude< keyof T, K > >, andkeyof Ton a type carrying astring index signature is
string | number— the literal member names are absorbed. EveryT extends BaseSchemainheritsBaseSchema's[key: string]: any(#5155), so thePartial< Omit< T, 'type' > >half of the intersection rebuilds a type holding the indexsignature and none of the named members. The explicit
{ type: T['type'] }half is the onlyreason the count is 1 rather than 0.
This is the same collapse #6151 fixed in a heritage clause and #6269 fixed in property
position. Here it is in a generic mapped-type alias, which is why neither card's guard
sees it: #6151's walks the
LayoutSchemaunion, #6269's readsObjectViewSchema's two slotproperties.
What it costs today: nothing measurable — the type has no consumers
Grepped across
packages/,apps/,examples/,content/anddocs/(excludingnode_modules): two occurrences of the identifier, and both are the declaration itself —packages/types/src/index.ts:905(source) andpackages/types/dist/index.d.ts:130(its ownemitted copy). No call site, no re-export under another name, no documentation page, no test.
Its doc comment says "Useful for partial schema definitions in editors"; nothing in this
repo's editors uses it.
So the harm is not a live miscompile — it is a published, exported type that promises
{ type } & everything-else-optionaland delivers{ type } & any, available for the nextconsumer to adopt and be silently unprotected by.
Options, none ruled here
capability — the startup-scope reading is that a declared-and-unconsumed surface does not
earn a repair. Cost: it is exported from
@object-ui/types, so an out-of-tree consumercould exist; needs the usual removal route (a
minorunder this repo's no-majorconvention, or a deprecation window).
tableandformslots declare ZERO properties — the same Omit-under-index-signature collapse as #6151, in property position #6269 way. Not directly portable: finding(types): ObjectViewSchema'stableandformslots declare ZERO properties — the same Omit-under-index-signature collapse as #6151, in property position #6269 could spell out an explicit keylist because
ObjectGridSchemaandObjectFormSchemaare concrete.There is generic,so there is no literal key list to write. A repair would need something like
{ type: T['type'] } & { [K in keyof T as K extends 'type' ? never : K]?: T[K] }, whichcollapses for the same
keyof Treason, or aBaseSchema-free member source — i.e. iteffectively waits on option 3.
[key: string]: anyleaves 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 indexsignature at the root and closes this class everywhere at once — at which point this alias
starts working as written with no edit at all.
My reading is that 1 and 3 are the live pair, and they are compatible: retiring an
unconsumed alias is cheap now, and if it is kept instead, #5155 fixes it for free later. What
is not defensible is leaving it declared, published, collapsed, and unpinned.
Refs: #6269 (same mechanism, property position — this was measured in its PR) · #6151 (same
mechanism, heritage clause) · #5155 (the root index signature) · #6143 (where the class first
surfaced).
Generated by Claude Code