Add generic type parameter for `useFocusZone` container ref
The type of the containerRef returned from useFocusZone is RefObject<HTMLElement>. This prevents it from being assigned to any actual HTML elements, because HTML element ref prop types are invariant[^1]. If you attempt to do the following you'll get a TypeScript error:
const {containerRef} = useFocusZone()
return <div ref={containerRef}>...</div>
Type 'RefObject<HTMLElement>' is not assignable to type 'LegacyRef<HTMLDivElement> | undefined'.
Type 'RefObject<HTMLElement>' is not assignable to type 'RefObject<HTMLDivElement>'.
Property 'align' is missing in type 'HTMLElement' but required in type 'HTMLDivElement'.
Currently the only workaround is a typecast (containerRef as RefObject<HTMLDivElement>) or defining the ref outside the hook using useRef.
This could easily be fixed by adding a generic type parameter to useFocusZone so that the consumer could define an element type to be used, for example:
const {containerRef} = useFocusZone<HTMLDivElement>()
return <div ref={containerRef}>...</div>
[^1]: This is surprising, but it happens because HTML element ref props accept both object and callback refs. Thus they are typed as (with some simplification) (element: T | null) => void | {current: T | null}. The function type makes T contravariant while the object type makes T covariant, resulting in an invariant type that accepts only exactly T. For more details on covariance and contravariance see the TypeScript documentation.
Thanks for reporting. Looks like an open source contributor spun up a PR https://github.com/primer/react/pull/5468 so I'm moving this to "In review" for the team to help get over the line.
Hi! This issue has been marked as stale because it has been open with no activity for 180 days. You can comment on the issue or remove the stale label to keep it open. If you do nothing, this issue will be closed in 7 days.