Reputation: 282825
I can't figure out the syntax to make this component generic. The signature is:
const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select({onChange,options: _options,value,...props}, forwardedRef) {
...
}
But SelectProps
is actually generic:
type SelectProps<T> = { ... }
So the type of Select
should be:
type SelectComponent<T> = ForwardRefExoticComponent<PropsWithoutRef<SelectProps<T>> & RefAttributes<HTMLSelectElement>>
(That's from the return value of forwardRef
)
But I can't figure out how to add the <T>
to const Select = ...
This doesn't work:
const Select: SelectComponent = forwardRef...
Because "TS2314: Generic type 'SelectComponent' requires 1 type argument(s)." but I don't want to specify the type argument, I want the caller to specify it.
How do I type this?
Upvotes: 1
Views: 277
Reputation: 11571
The simplest thing is to cast the result of forwardRef()
back into its original form before being wrapped. This requires you to refactor a bit to pull out the inner component and make it fully generic:
type SelectProps<T> = {
onChange: (newValue: T) => null;
options: any;
value: T;
};
function SelectInner<T>(
{ onChange, options: _options, value, ...props }: SelectProps<T>,
forwardedRef: React.ForwardedRef<HTMLSelectElement>
) {
return null;
}
const Select = forwardRef(SelectInner) as typeof SelectInner;
Another option is to augment the forwardRef
type definition to be more what you want, which is perhaps more useful if you intend to be doing this a lot. If you look closely, this is essentially the same thing as the option above, just defined in a way such that it applies to all forwardRef()
instances.
declare module 'react' {
function forwardRef<T, P = {}>(
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
}
type SelectProps<T> = {
onChange: (newValue: T) => null;
options: any;
value: T;
};
const Select = forwardRef(
(
{ onChange, options: _options, value, ...props }: SelectProps<T>,
forwardedRef: React.ForwardedRef<HTMLSelectElement>
) => {
return null;
}
);
Note: in either case using React.ForwardedRef
requires the latest @types/react
to be installed. Source
Upvotes: 1