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:
- withErrorListener wraps a base component that has the uniqueId property. The uniqueId property is created by another Higher Order Component, withId.
- 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[]}
- 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