Reputation: 9635
I'm trying to integrate reselect into some redux connected components. According to the docs for reselect (https://github.com/reduxjs/reselect#sharing-selectors-with-props-across-multiple-component-instances), when dealing with props passed in, to ensure the selector is properly memoized, you basically create a factory function for createSelector
, so every component calling connect()
gets its own selector with its own memoized state. This makes sense, and I have it working in TypeScript:
interface UploadProfileOwnProps {
someValue: string;
}
const getUploadProfile = (state: AppState, props: UploadProfileOwnProps): UploadProfileState => state.uploadProfile;
const getCustomer = (state: AppState, props: UploadProfileOwnProps): CustomerState => state.customer;
const uploadProfileSelectorFactory = () => createSelector(getCustomer, getUploadProfile,
(customerState, profile)=>({
customerId: customerState.customerId,
...profile
}));
type uploadProfileSelectorProps = ReturnType<ReturnType<typeof uploadProfileSelectorFactory>>;
const connectUploadProfile = connect(()=>{
const selector = uploadProfileSelectorFactory();
const mapStateToProps = (state: AppState, props: UploadProfileOwnProps): uploadProfileSelectorProps =>{
return selector(state, props);
};
return mapStateToProps;
}, UploadProfileActions);
The OwnProps
is just a placeholder, the state doesn't depend on it at all in this case, but in others it might. The double ReturnType<ReturnType<>>
construct bugs me a bit, but it gets the job done. Now, the actual question:
uploadProfileSelectorFactory
lacks an explicit return type. I have eslint configured to yell at me in that case. How can I convince TypeScript to tell it that the factory function's return type is whatever createSelector()
returns? It does properly infer the return type, so it's not causing any issues outside of eslint being cranky. I see a few options:
CreateSelector
, which in this case would be a rather messy OutputParametricSelector<S, P, T, (res1: R1, res2: R2) => T>
that I would have to remember to change if I add another selector.createSelector()
without actually calling it. This might be able to be done with explicitly defining the return type of the combiner function, although I haven't tried it. This would probably also get rid of the ReturnType<ReturnType<>>
business, at the cost of having me keep the combiner function and its return type in sync.re-reselect
or similar into the mix, and hope it doesn't have that issue.I'm hoping for some combination of 5 and 3, although I'll take 4 if needed. Unfortunately every example I've found just uses type inference and never deals with this.
Upvotes: 1
Views: 2005
Reputation: 42188
You do know what createSelector
returns, because ultimately it's just a selector that selects the customerId
and profile
. The selectors created by reselect have to extra properties like recomputations
and resetRecomputations
, but these don't matter for the purposes of react-redux. We just want to know what arguments the selector takes and what it returns.
This is what you are selecting:
type Selected = UploadProfileState & Pick<CustomerState, 'customerId'>
So you can use that instead of ReturnType<ReturnType<typeof uploadProfileSelectorFactory>>
The return type of uploadProfileSelectorFactory
would be:
(state: AppState, props: UploadProfileOwnProps) => Selected;
But that is long-winded and we have those same two arguments in multiple places, so if you want to you could define a helper type:
type UploadProfileSelector<Return> = (state: AppState, props: UploadProfileOwnProps) => Return;
or more generalized:
type ComponentSelector<Props, Return> = (state: AppState, props: Props) => Return;
type UploadProfileSelector<Return> = ComponentSelector<UploadProfileOwnProps, Return>;
Which you can use as the return type:
const uploadProfileSelectorFactory = (): UploadProfileSelector<Selected> =>
Upvotes: 1