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 ponent.
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 plains 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.
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 ponent.
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 plains 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.
Share Improve this question edited Feb 25, 2020 at 23:14 AmerllicA 32.8k17 gold badges143 silver badges167 bronze badges asked Feb 21, 2020 at 17:28 AbulafiaAbulafia 1,76516 silver badges26 bronze badges 2-
Actually, just pass
{...props}
on. – Jonas Wilms Commented Feb 21, 2020 at 17:50 - Yeah, that's what I've acutally been doing. But I'd like to know what the solution is, or whether it's just a problem with React types - other experiements don't seem to have the same problem, but it could just be a plexity issue. – Abulafia Commented Feb 21, 2020 at 20:19
2 Answers
Reset to default 6 +50Actually, Typescript is catching a Problem with your ponent:
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 ponent.
<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 plain:
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 plains on this line.
{...rest}
/>
);
}
return <Child {...props} />;
};
return Connector;
}
To your code stops to plain, you need to fix how you are passing the props to the Child
ponent. He accepts props
of type T
. But rest
in your case is not of type T
(or at least TS piler 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 piler 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!