Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

reactjs - Experiencing compile error with a higher order component's mapState function that returns an object within an object

30/11/2019 Updated the code sandbox with the solution here.

Original Question asked on 28/11/2019 I am trying to learn Higher Order Components but struggling to get a Higher Order Component connected to the redux store and router. Hoping that someone can help with a Higher Order Component type compilation problem I am struggling with relating to mapStateToProps.

I am trying to create a higher order component, withErrorListener. It has the following features:

  1. withErrorListener wraps a base component that has the uniqueId property. The uniqueId property is created by another Higher Order Component, withId.
  2. withErrorListener returns a wrapper class component that listens on the redux store and filters errors notified for that the base component. If there are no errors to display it renders the base component. I am filtering errors in redux state using mapStateToProps(state, ownProps) => StateProps, where StateProps = { error: IApiFailure[]}
  3. wthErrorListener renders the errors and dispatches a _clear_error_ action when the component is unmounted by clicking home button on error rendered page. This means that the higher order component also requires RouterComponentProps dependency.

It is intended to be used as follows NewComponent = withErrorListener(withId(BaseComponent)).

The problem that I am encountering is that Typescript raises the compile error that type IApiFailure cannot be found in the ErrorListener HoC class when it is assigned to the react-redux connect function. IApiFailure is a type contained within the StateProps return type for the mapStateToProps function. The only way that I can get it to compile is to cast to the any type.

The HoC is connected to the store with the following code snippet below. The full code for the HoC ErrorListener class can be found here. I have also included the error information below ...

Subsequently, I cannot connect the connected component to the withRouter function. If I cast the Hoc ErrorListener to any type in the connect function, then compilation succeeds but the uniqueId property in OwnProps is undefined. This is used to filter the error store.

  /**
   * Redux properties
   */
  type StateProps = {
    error: FailureNotify[];
  };

  /**
   * Function to return subset of store state that filters errors for the wrapped component via uniqueId property
   * @param state  The root state
   * @param ownProps  Properties passed to wrapped component according to `https://react-redux.js.org/next/using-react-redux/connect-mapstate#ownprops-optional`
   * @returns  StateProps type that contains a list of filtered errors of type FailureNotify.
   */
  const mapStateToProps = (
    state: RootState,
    ownProps: HocProps 
  ): StateProps => {
    console.log(`withErrorListener mapStateToProps => ${ownProps.uniqueId}`);
    return {
      error: filterErrors(state.errors, ownProps)
    };
  };

  const dispatchProps = {
    clearError: clearErrorAction
  };

  /**
   * Type declarations
   */
  type TStateProps = ReturnType<typeof mapStateToProps>;
  type TDispatchProps = typeof dispatchProps;
  type HocProps = BaseProps & TStateProps & TDispatchProps;

const ConnectedHoc = connect<
    TStateProps,
    TDispatchProps,
    ExpectedProps,
    RootState
  >(
    mapStateToProps,
    dispatchProps
  )(ErrorListener); // this raises the error below...unless I cast it as any

  return ConnectedHoc;
  // const RouteHoc = withRouter(ConnectedHoc);

  // return RouteHoc;

The compile error that I receive is below. I think typescript is not recognising the type embedded within the type returned by mapStateToProps, i.e. IApiFailure[] type is unrecognised in HocProps. Is this not automatically achieved when using ReturnType<typeof mapStateToProps>?

Argument of type 'typeof ErrorListener' is not assignable to parameter of type 'ComponentType<Matching<StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; }, HocProps>>'.
  Type 'typeof ErrorListener' is not assignable to type 'ComponentClass<Matching<StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; }, HocProps>, any>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'Matching<StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; }, HocProps>' is not assignable to type 'Readonly<HocProps>'.
        Type 'P extends "error" | "clearError" ? (StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; })[P] extends HocProps[P] ? HocProps[P] : (StateProps & { ...; })[P] : HocProps[P]' is not assignable to type 'HocProps[P]'.
          Type 'HocProps[P] | ((StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; })[P] extends HocProps[P] ? HocProps[P] : (StateProps & { ...; })[P])' is not assignable to type 'HocProps[P]'.
            Type '(StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; })[P] extends HocProps[P] ? HocProps[P] : (StateProps & { ...; })[P]' is not assignable to type 'HocProps[P]'.
              Type '(StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; })[P] | HocProps[P]' is not assignable to type 'HocProps[P]'.
                Type '(StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; })[P]' is not assignable to type 'HocProps[P]'.
                  Type 'StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; }' is not assignable to type 'HocProps'.
                    Type 'StateProps & { clearError: (fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<constants.CLEAR_ERROR, ClearError>; }' is not assignable to type 'BaseProps'.
                      Type '(FailureNotify[] extends HocProps["error"] ? HocProps["error"] : FailureNotify[]) | ((fromAction: string, fromComponent: string, history?: History<any>, navigateTo?: string) => PayloadAction<...> extends HocProps["clearError"] ? HocProps["clearError"] : (fromAction: string, fromComponent: string, history?: History<....' is not assignable to type 'HocProps[P]'.
                        Type 'FailureNotify[] extends HocProps["error"] ? HocProps["error"] : FailureNotify[]' is not assignable to type 'HocProps[P]'.
                          Type 'FailureNotify[] | HocProps["error"]' is not assignable to type 'HocProps[P]'.
                            Type 'FailureNotify[]' is not assignable to type 'HocProps[P]'.ts(2345)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

When you're injecting Props by using mapStateToProps and dispatchProps in a connect you have to subtract these props from the BaseProps generic type by using the Diff helper type. Moreover, if you want to use these injected Props in the Hoc component you have to pass them as the Component type argument. e.g. React.Component<InjectedProps>.

This is a reason for the errors.

So in your example props are:

type InjectedProps = TStateProps & TDispatchProps;

type HocProps = Diff<BaseProps, InjectedProps>;

class Hoc extends React.Component<InjectedProps> { ... }

const ConnectedHoc = connect<
    TStateProps,
    TDispatchProps,
    HocProps,
    RootState
  >(
    mapStateToProps,
    dispatchProps
  )(Hoc);

Also, please check the same discussion in the react-redux-typescript-guide


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...