简体   繁体   中英

Spread operator with React Props in Typescript (error: ...could be instantiated with a different subtype of constraint {})

I am trying to write a Higher Order Component in React using Typescript that receives the props, 'consumes' one of them and then passes on the remainder to the Child component.

function testConnect<T>(Child: React.ComponentType<T>): React.ComponentType<T> {

  type InitialState = {
    iS: StoreShape.State;
  };

  type LAP = InitialState & T;

  const Connector = (props: LAP) => {
    const { iS, ...rest } = props;
    // do something with iS
    return (
      <Child // Visual Studio complains about this line.
        {...rest}
      />
    );
  };

  return Connector;
}

However this fails with the error: 'Pick<LAP, Exclude<keyof T, "iS">>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{}'

I would like to know not only if there's anything I can do about it, but also why this happens at all.

Actually, Typescript is catching a Problem with your component:

 const Component = testConnect(({ iS }) => <div>{iS}</div>);

 <Component iS={...} />

So you either have to

(a) pass on all props (instead of just rest) to the component.

   <Child   {...props} />

(b) Make sure that no prop named "iS" can be passed in by excluding the key iS from T:

 testConnect<T>(Child: React.ComponentType<Omit<T, "iS">>>): React.ComponentType<T> {

Here is a solution to your problem, not the most elegant one, but it stops to complain:

type InitialState = {
    iS: StoreShape.State;
};

function testConnect<T>(Child: React.ComponentType<T>): React.ComponentType<T> {
    const Connector = (props: InitialState & Exclude<T, "iS">) => {
        const isT = (object: any): object is T => {
            return !("iS" in object);
        };

        const { iS, ...rest } = props;

        if (isT(rest)) {
            return (
                <Child // Visual Studio complains on this line.
                    {...rest}
                />
            );
        }

        return <Child {...props} />;
    };

    return Connector;
}

To your code stops to complain, you need to fix how you are passing the props to the Child component. He accepts props of type T . But rest in your case is not of type T (or at least TS compiler is not smart enough to figure it out), its type is Pick<LAP, Exclude<keyof T, "iS">> because of the rest operator you used.

My solution idea is just to let the compiler knows that rest is of type T , using a custom type guard function , but you could use other approaches, like a type casting:

<Child
  {...((rest as unknown) as T)}
/>

I hope it helps you!

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