1

I have this Higher order component written down:

type WrappedComponentConditionallyHidden =
  <P>(WrappedComponent: React.ComponentType<P>) => React.FC<P & { isHidden: boolean }>;
    
const hideConditional: WrappedComponentConditionallyHidden = (WrappedComponent) => {

  return (props) => {
    // typeof props is inferred here correctly: P & { isHidden: boolean }        

    // Don't want to pass down "isHidden"
    const { isHidden, ...passedProps } = props; 

    if (props.isHidden) {
      return null;
    } else {
      return <WrappedComponent {...passedProps} /> 
      // Passing { ...props } works but ^^^^ this fails
    }
  }
}

I get this Typescript error:

 Type 'Pick<PropsWithChildren<P & { isHidden: boolean; }>, "children" | Exclude<keyof P, "isHidden">>' is not assignable to type 'IntrinsicAttributes & P & { children?: ReactNode; }'
  Type 'Pick<PropsWithChildren<P & { isHidden: boolean; }>, "children" | Exclude<keyof P, "isHidden">>' is not assignable to type 'P'.
    'Pick<PropsWithChildren<P & { isHidden: boolean; }>, "children" | Exclude<keyof P, "isHidden">>' is assignable to the constraint of type 'P', but 'P' could be instantiated with a different subtype of constraint '{}'.

For me, 'Pick<PropsWithChildren<P & { isHidden: boolean; }>, "children" | Exclude<keyof P, "isHidden">> should be equal to PropsWithChildren<P> and hence it should work. Any ideas why it wouldn't?

1
  • Sounds like the compiler's inference is getting confused about the types. I don't know if this will work, but you could try offering it some help: const { isHidden, ...passedProps }: { isHidden: boolean, passedProps: PropsWithChildren<P> } = props;. At the very least you might get a more helpful compiler error telling you why the spread variable can't be cast. Commented Jun 15, 2021 at 16:30

1 Answer 1

1

The general problem is explained in great details in this answer. And if you're looking for more grounded example of this problem you may look for this answer.

And I believe the most clear explanation is done here:

TS isn't capable of the higher-order reasoning needed to understand that Exclude<T, k> & { [k]: T[k] } is equivalent to T. You can only make that determination through understanding what Exclude and & actually do at a higher level, but from TS's point of view, Exclude is just a type alias

As a solutions you may just hint TS about the real type of passedProps:

const hideConditional = function <P>(WrappedComponent: React.ComponentType<P>): React.FC<P & { isHidden: boolean }> {
  return (props) => {
    const { isHidden, ...passedProps } = props; 

    if (props.isHidden) {
      return null;
    } else {
      return <WrappedComponent {...passedProps as P} /> 
    }
  }
}

playground link

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.