HoC for context consumer causing type error with c

2019-08-28 04:57发布

问题:

I am having an issue wherein I attempt to write a HoC in order to wrap components that should have access to a particular context via newly injected props.

There is a Gist related to a tutorial which basically contains the following snippet:

export function withAppContext<
    P extends { appContext?: AppContextInterface },
    R = Omit<P, 'appContext'>
>(
    Component: React.ComponentClass<P> | React.StatelessComponent<P>
): React.SFC<R> {
    return function BoundComponent(props: R) {
        return (
            <AppContextConsumer>
                {value => <Component {...props} appContext={value} />}
            </AppContextConsumer>
        );
    };
}

Similarly there is another snippet here on SO which looks as follows:

function withTheme<P extends ThemeAwareProps>(Component: React.ComponentType<P>) {
    return function ThemedComponent(props: Pick<P, Exclude<keyof P, keyof ThemeAwareProps>>) {
        return (
            <ThemeContext.Consumer>
                {(theme) => <Component {...props} theme={theme} />}
            </ThemeContext.Consumer>
        )
    }
}

They are pretty much the same and apparently worked once, however this is no longer the case. Using both variants the child component Component in the render prop of the consume is underlined with the following error message:

Type '{ exploreContext: IExploreContextStore; }' is not assignable to type 'IntrinsicAttributes & P & { children?: ReactNode; }'.
    Property 'exploreContext' does not exist on type 'IntrinsicAttributes & P & { children?: ReactNode; }'.

My sample code where is issue occurs is as folows:

type WithExploreContext = { exploreContext?: IExploreContextStore };

export function withExploreContext<P extends WithExploreContext>(ChildComponent: ComponentType<P>) {
    return function WrappedComponent(
        props: Pick<P, Exclude<keyof P, keyof WithExploreContext>>
    ) {
        return <Consumer>{(value) => <ChildComponent {...props} exploreContext={value} />}</Consumer>;
    };
}

I pretty much have no clue what's the issue with these snippets and why they no longer work as expected.

回答1:

Spreading the props to the child component alone wasn't sufficient. It started working right after casting it to P using {...props as P}.

Starting with 3.2 the behaviour of the spread operator for generics has changed. Apparently the type of props gets erased as a negative side effect, but you can work around that by casting it back to P using {...props as P} when spreading back into the wrapped component.