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
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.
Found while turning
noImplicitAnyon for #4353 (PR #4417). Observation-class: no user-visible symptom, no fix proposed here. It is a type-safety hole thatnoImplicitAnycannot catch, which is why turning that flag on does not close it.What
A renderer written like this looks fully typed and is not:
forwardRef's render function takesPropsWithoutRefof the props type, defined in@types/react(19.2.18) as:An index signature puts
stringintokeyof Props, so"ref" extends keyof Propsis always true and theOmitbranch always runs.Omitover 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 }, andschemaresolves through the index signature toany.Measured in situ rather than inferred: inserting
const probe: null = schema;intoaction-bar.tsxraised no error, which understrictNullChecksonlyanydoes.Why this is worse than a plain missing annotation
noImplicitAnydoes not see it. Theanyis supplied explicitly by the index signature, so nothing is "implicit" and noTS7006/TS7031is raised forschema. What finding(components): noImplicitAny is off package-wide, and 26 renderer signatures depend on it #4353 saw was only the second-order damage — untyped callbacks downstream of the untypedschema.ActionSchemawhile being written againstUIActionSchema(sibling finding), a contradiction that produced four compiler errors the instant those values were given a real type.Scope
11 files in
packages/componentsinstantiateforwardRefwith a props type carrying[key: string]: any:Not swept beyond this package. The pattern is not package-specific, so other packages'
forwardRefrenderers 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:
PropsWithoutRef, so declared props survive. Smallest diff, one line per component; leaves the trap in place for the next author.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.
Generated by Claude Code