Here is the best practice, as far as I know.
Let's assume your component is called ShowAnyProps, and it takes any props, and you don't want to enforce any limitations on what kind of props can be passed.
The ShowAnyProps Component
type IProps = Record<string, unknown>;
export const ShowAnyProps: React.FC<IProps> (props) {
// Use props here...
}
props is an object with string keys, but you don't know its values. Object keys are always strings, and props is always an object, because that's how JSX works.
Inside the component, you'll want to do some type narrowing to handle all possible kinds of values:
string
number
boolean
null
undefined
object (and other things that are objects):
array
function
- class instances
Symbol
bigint
Map
Set
Given the wide variation of things that can be passed in, you might want to limit the props, to only "regular" values. Maybe JSON-like values only:
import type { JSONValue } from 'type-fest';
type IProps = Record<string, JsonValue>;
Usage of ShowAnyProps
Material-UI has a good convention. Instead of passing all props to the component, the intended props are passed on a sub-object. See inputProps on TextField, for example.
So the usage would look like this:
interface IProps {
// own props
showAnyPropsProps: Record<string, JsonValue>;
};
export const UsageComponent: React.FC<IProps> (props) {
// ...
return (
<div>
{/* ... other stuff ... */}
<ShowAnyProps {...props.showAnyPropsProps} />
</div>
);
}