I am using Typescript and I would like to have prop validation on a generic component. I would like to create a Field component which renders a label, an error message and a component which is passed as a prop. I had multiple attempts to make it work but one part or the other fails either way. I've also read through dozens of articles but I couldn't find anyone facing the exactly same issue.
This is what I've got so far:
interface IFieldProps<P> {
is: React.ComponentType<P>;
}
class Field<P> extends React.PureComponent<IFieldProps<P>> {
render() {
const { is, ...rest } = this.props;
const Component = is;
return (
<div>
<Component {...rest} />
</div>
);
}
}
interface ITextInputProps {
value: string
}
class TextInput extends React.PureComponent<ITextInputProps> {
render() {
return (
<input {...this.props} />
);
}
}
const render = () => <Field is={TextInput} />;
The typescript compiler is not yelling about that the prop value
is missing in the render
method, So I will make the IFieldProps
to extend from P
and that should make it work:
interface IFieldProps<P> extends P {
is: React.ComponentType<P>;
}
The compiler now says that "An interface may only extend a class or another interface". Okay, then I will make this a type, no worries:
type IFieldProps<P> = P & {
is: React.ComponentType<P>;
}
Now everything is screwed up. It yells on ...rest
that "Rest types may only be created from object types". It yells on <Component />
that "JSX element type 'Component' does not have any construct or call signatures". And finally it yells on <Field />
that value
prop is missing - and in the meantime the autocomplete on the props stops working in vscode. I thought that if I would have some constraints on P (eg P extends {}
) that would fix some of the issues, but it didn't.
How can I make a generic component that inherits the props of a component passed as a prop? Is still something overcomplicated? How other people solve this issue?
It works when wrapped component props are kept in another prop (as opposed to being mixed with is
):
interface IFieldProps<P> {
is: React.ComponentType<P>;
props: P;
};
class Field<P> extends React.PureComponent<IFieldProps<P>> {
render() {
const { is, props } = this.props;
const Component = is;
return (
<div>
<Component {...props} />
</div>
);
}
}
interface ITextInputProps {
readonly value: string;
}
class TextInput extends React.PureComponent<ITextInputProps> {
render() {
return (
<input {...this.props} />
);
}
}
const render = () => <Field is={TextInput} props={{ value: ''} } />;
I wasn't able to find the source of the error based on the message:
Type 'Pick & Readonly>, "children" | Exclude>' is not assignable to type 'IntrinsicAttributes & LibraryManagedAttributes["is"], P & { children?: ReactNode; }>'. Type 'Pick & Readonly>, "children" | Exclude>' is not assignable to type 'LibraryManagedAttributes["is"], P & { children?: ReactNode; }>'.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.