简体   繁体   中英

Generic typescript component inherit component props

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM