简体   繁体   中英

React HOC has a Typescript error as a class component but not as a function component

typescript - v3.9.7

I have a HOC that pass additional props to the component:

import React from 'react';

interface IWithOwner {
    owner: string;
}

type IWithoutOwner<T extends IWithOwner> = Omit<T, keyof IWithOwner> & {owner?: never};

// CORRECT
export function withOwnerOne<TProps extends IWithOwner>(Component: React.ComponentType<TProps>) {
    const ComponentWithOwner: React.FunctionComponent<IWithoutOwner<TProps>> = (props) => {
        return (
            <Component
                owner="Tom"
                {...props}
            />
        );
    }

    return ComponentWithOwner;
}

// ERROR
export function withOwnerTwo<TProps extends IWithOwner>(Component: React.ComponentType<TProps>) {
    return class extends React.Component<IWithoutOwner<TProps>> {
        render() {
            return (
                // '{ owner: string; } & Readonly<IWithoutOwner<TProps>> & Readonly<{ children?: ReactNode; }>'
                // is assignable to the constraint of type 'TProps', but 'TProps' could be
                // instantiated with a different subtype of constraint 'IWithOwner'.
                <Component
                    owner="Tom"
                    {...this.props}
                />
            )
        }
    }
}

withOwnerOne that uses Function component works correctly. While withOwnerTwo that uses class component has an error. Why do they work differently?

Playground link

Same case with functions works correctly

I believe the problem is in props type.

In first example, FunctionComponent , type of props is: IWithoutOwner<TProps> & { children?: ReactNode };

interface IWithOwner {
  owner: string;
}

type IWithoutOwner<T extends IWithOwner> = Omit<T, keyof IWithOwner> & {
  owner?: never;
};

function withOwnerTwo<TProps extends IWithOwner>(
  Component: React.ComponentType<TProps>
) {
  const ComponentWithOwner: React.FunctionComponent<IWithoutOwner<TProps>> = (
    props
  ) => {

    //  React.PropsWithChildren<IWithoutOwner<TProps>>   -->    IWithoutOwner<TProps> & { children?: ReactNode };
    const x = props; 
    return <Component owner="Dmitry" {...props} />;
  };

  return ComponentWithOwner;
}

In second, props has a bit different type:

Readonly<IWithoutOwner<TProps>> & Readonly<{children?: React.ReactNode;}>

function withOwnerOne<TProps extends IWithOwner>(
  Component: React.ComponentType<TProps>
) {
  class Wrapper extends React.Component<IWithoutOwner<TProps>> {
    render() {
      //Readonly<IWithoutOwner<TProps>> & Readonly<{children?: React.ReactNode;}>
      const props = this.props; 

      return <Component owner="Dmitry" {...props} />;
    }
  }

  return Wrapper;
}

Now, you may think that the problem is in Readonly modifier. Readonly does not cause the error. I strongly believe, that error is caused by contravariance.

Because type of props is taken from component context, and props generic is placed in differen positions in above components.

I'm open to criticizm, so feel free to point my mistakes.

Covariance/variance/invariance - are too complicated topic for me, I more feel it than understand. So, I will be happy if you point me the correct way.

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