Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Pass component with arbirtrary/generic props to react-redux connect function in TypeScript

I'm trying to connect a React element with react-redux that has arbitrary/generic props, but it's not compiling correctly. I've tried using JSXElementConstructor and (new (props: Props) => React.Component<Props, any>) instead of ComponentType, but got a different error about how Props could be instantiated with a different type. Here's the code. Any help would be appreciated.

https://github.com/piotrwitek/react-redux-typescript-guide/issues/55 looks related, but that seems to be about the props having a generic, instead of the props themselves being a generic.

Code

import React from "react";
import { connect } from "react-redux";

interface StoreState {
  someState: number;
}

// Tried `React.JSXElementConstructor<Pros>`
// and `(new (props: Props) => React.Component<Props, any>)`
// instead of `React.ComponentType<Props>`, but got a different error.
function createWrapperComponent<Props>(
  componentElement: React.ReactElement<
    Props,
    React.ComponentType<Props> & { update(props: Props): void }
  >
) {
  const props = componentElement.props;
  const UnconnectedComponent = componentElement.type;
  const ConnectedComponent = connect<StoreState, {}, Props, StoreState>(
    (state) => state
  )(UnconnectedComponent);

  return class WrapperComponent extends React.Component<any> {
    static update = UnconnectedComponent.update;
    render() {
      return <ConnectedComponent {...props} />;
    }
  };
}

Playground Link: Provided

like image 310
Nathan Bierema Avatar asked May 16 '20 14:05

Nathan Bierema


2 Answers

Update: Here's an approach that avoids the initial cast to any. Now I'm casting the props passed to ConnectedComponent instead. Investigating the type issue there might reveal whether there are more potential issues than the one I mentioned.


What you're doing is not sound. If Props were { someState: string }, your UnconnectedComponent would be expecting its someState prop to be a string, but it would be a number.

If stateProps and dispatchProps should override ownProps, which is what your code is doing now, I'm not sure whether you're going to be able to get the types to work automatically the way you want. TypeScript can't figure out that the following is sound:

function a<Props>(
  b: { [K in keyof Props]: K extends keyof StoreState ? StoreState[K] : Props[K] }
): Omit<Props, keyof StoreState>
{
  return b;
}

Maybe you should bite the bullet and use any. Here's a sample of how you might try to make the caller-facing types safer.

If you instead want ownProps to override stateProps, you would want to provide a mergeProps argument to connect.

However, be warned that when you create a ReactElement via JSX, the type field has type any, which of course won't give you type safety if you then call createComponent.

like image 168
J. Bierema Avatar answered Sep 19 '22 06:09

J. Bierema


Your Props doesn't refer to any types defined. So I guess leave it blank is ok?

function createComponent(componentElement: React.ReactElement<{}, React.ComponentType>) {
  const props = componentElement.props;
  const UnconnectedComponent = componentElement.type;
  const ConnectedComponent = connect<StoreState, {}, {}, StoreState>(state => state)(UnconnectedComponent);

  return <ConnectedComponent {...props} />
}
like image 21
tcf01 Avatar answered Sep 20 '22 06:09

tcf01