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
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
finding(components): forwardRef + a props index signature silently erases every declared prop type — 11 renderers affected #4422
Found while turning noImplicitAny on for #4353 (PR #4417). Observation-class: no user-visible symptom, no fix proposed here. It is a type-safety hole that noImplicitAnycannot catch, which is why turning that flag on does not close it.
What
A renderer written like this looks fully typed and is not:
constActionBarRenderer=forwardRef(({ schema, className, ...props},ref)=>{/* schema is `any` here */},);// instantiated as forwardRef with props { schema: ActionBarSchema; [key: string]: any }
forwardRef's render function takes PropsWithoutRef of the props type, defined in @types/react (19.2.18) as:
An index signature puts string into keyof Props, so "ref" extends keyof Props is always true and the Omit branch always runs. Omit over a type carrying a string index signature keeps only the index signature — every declared property is erased. The render function therefore receives { [x: string]: any }, and schema resolves through the index signature to any.
Measured in situ rather than inferred: inserting const probe: null = schema; into action-bar.tsx raised no error, which under strictNullChecks only any does.
Why this is worse than a plain missing annotation
It is silent. The props type is right there in the source, so the component reads as typed to every reviewer and to every tool.
It hides declaration/implementation drift indefinitely. That is not hypothetical: it is exactly what let the action renderers declare the deprecated ActionSchema while being written against UIActionSchema (sibling finding), a contradiction that produced four compiler errors the instant those values were given a real type.
Scope
11 files in packages/components instantiate forwardRef with a props type carrying [key: string]: any:
Not swept beyond this package. The pattern is not package-specific, so other packages' forwardRef renderers are worth the same grep before anyone sizes a fix.
Directions, not a recommendation
Deliberately not choosing here — this wants a decision, and the trade differs per component:
Annotate the render function's parameter directly in addition to the type argument. The explicit annotation is not routed through PropsWithoutRef, so declared props survive. Smallest diff, one line per component; leaves the trap in place for the next author.
Drop the index signature from the props type and name the pass-through props. Fixes it structurally and is what commandment Fix documentation deployment for www.objectui.org #6 actually asks for, but the registry genuinely spreads arbitrary props onto the underlying Shadcn component, so each component needs its real prop surface worked out.
A shared props helper for registered renderers that is index-signature-free at the declaration boundary, with the spread handled in one place.
Whichever is chosen, note that turning strictness flags on will keep reporting clean on these files either way — the hole is invisible to the compiler by construction, so it needs a lint rule or a review convention to stay closed.
Activity
refactor(components): action keys publish UIActionSchema; forwardRef …
fix(types): DashboardRenderer and ListView serve their declared props…
fix(react): SchemaRenderer states its real contract — typed schema, d…