Reputation: 25310
I have a sample placeholder component that consumes two required props:
type ComponentProps = {
foo: string;
bar: number;
}
const Component = (props: ComponentProps) => <></>;
I'm trying to write a higher order component that provides some certain props, and thus removes them from the required props type for further use:
type WrapperProvides = {
foo: string;
}
function withFoo<T extends WrapperProvides>(ToWrap: (componentProps: T) => JSX.Element) {
type RemainingProps = Omit<T, keyof WrapperProvides>;
const providedFoo = "foo";
return (props: RemainingProps) => <ToWrap foo={providedFoo} {...props} />;
}
This would then be used elsewhere without needing to specify foo
:
const WrappedComponent = withFoo(Component);
const result = <WrappedComponent bar={42} />;
However, currently the return of withFoo()
yields the following error on <ToWrap .../>
:
Type '{ foo: string; } & Pick<T, Exclude<keyof T, "foo">>' is not assignable to type 'IntrinsicAttributes & T'.
Type '{ foo: string; } & Pick<T, Exclude<keyof T, "foo">>' is not assignable to type 'T'.
'{ foo: string; } & Pick<T, Exclude<keyof T, "foo">>' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'WrapperProvides'.(2322)
Functionality-wise, the code already works as expected, but I can't figure out what the type issue is here.
I've looked through related issues on "could be instantiated with a different subtype of constraint"
, but I feel I don't understand the problem conceptually.
A Typescript playground replicating the issue can be found here.
Upvotes: 1
Views: 90
Reputation: 249486
TS will not be able to follow that WrapperProvides & Omit<T, keyof WrapperProvides>
is the same as T
. You can either use a type assertion
function withFoo<T extends WrapperProvides>(ToWrap: (componentProps: T) => JSX.Element) {
type RemainingProps = Omit<T, keyof WrapperProvides>;
const providedFoo = "foo";
return (props: RemainingProps) => <ToWrap foo={providedFoo} {...props as any} />;
}
Or you could add a redundant type to the parameter of the component (WrapperProvides & Omit<T, keyof WrapperProvides>
). This type will ensure compatibility with the specified properties in your function, and should not have major drawbacks on the calling side as T
and WrapperProvides & Omit<T, keyof WrapperProvides>
should evaluate to the same type (or at least compatible types) on the calling side where T
is known
function withFoo<T extends WrapperProvides>(ToWrap: (componentProps: T | (WrapperProvides & Omit<T, keyof WrapperProvides>)) => JSX.Element) {
type RemainingProps = Omit<T, keyof WrapperProvides>;
const providedFoo = "foo";
return (props: RemainingProps) => <ToWrap foo={providedFoo} {...props} />;
}
Upvotes: 2