'How to make higher-order component generic?
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?
Solution 1:[1]
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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | jered |
