How to define a version of createSelector typed for my app?
Asked Answered
E

3

8

In redux-toolkit docs they suggest you to create the following definition to have proper types when you use useSelector hook:

export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;

This will save you of having to type all the selectors with the root state.

However, when it's time to define memoized selector with createSelector there are no instructions about how to do a similar thing. Is there any shortcut for this or I have to manually type every memoized selector?

Edessa answered 18/4, 2021 at 6:50 Comment(4)
What's the error? What do you expect?Buffet
Who says there is an error? I want to create a typed version of createSelector just like in the example I posted, where there is a version of useSelector hook typed for my specific appEdessa
Have you found solution to this?Syriac
no, not yet. If you do, please add it as an answerEdessa
C
1

I was working on a app that I wanted to do the same. For my needs, I just wanted a simple selector, so I did this:

// store.ts

// …omitted store configuration

export type RootState = ReturnType<typeof store.getState>;

// create a generic type called AppSelector
export type AppSelector<Return> = (state: RootState) => Return
// create a custom `createSelector` that uses the type above
export const createAppSelector = <R>(selector: AppSelector<R>): AppSelector<R> => selector

With the types above, we can do the following:

const selectTitle: AppSelector<string> = (state) => state.title;
// Note that using the AppSelector type requires you to always pass the return type argument
// It's much easier to use the createAppSelector function
// selectTitle and selectTitle2 accomplish the same thing
const selectTitle2 = createAppSelector((state) => state.title);

When inside createAppSelector, state will correctly have the RootState type.

The limitation of this form is that, unlike createSelector, createAppSelector only allows you to create a single selector and doesn't allow you to combine or mutate the selections.

Hopefully someday we'll have a way to const createAppSelector: TypedCreateSelector<RootState> = createSelector;

Crutchfield answered 9/1, 2022 at 12:17 Comment(2)
That looks neat. But why not just an alias rather an actual function? like this export const createAppSelector: <R>(selector: AppSelector<R>) => AppSelector<R> = createSelector;Edessa
The way createSelector is typed doesn't allow it to be an alias, unfortunately. If you try to alias it, you'll get an error saying that createSelector is not assignable to type <R>(selector: AppSelector<R>) => AppSelector<R>, due to all the overloads that createSelector has.Crutchfield
H
1

I know this question is years old now at this point but just in case anybody else comes across this. This is what I came up with:

export type TypedCreateSelector<State> = <
  SelectorsArray extends readonly Selector<State>[],
  Result,
>(
  ...args: Parameters<typeof createSelector<SelectorsArray, Result>>
) => ReturnType<typeof createSelector<SelectorsArray, Result>>;

So now You can have something like this:

export const createAppSelector: TypedCreateSelector<RootState> = createSelector;
export const createDraftSafeAppSelector: TypedCreateSelector<RootState> =
  createDraftSafeSelector;

Which is similar to the way Redux-Toolkit wants you to create typed versions of the hooks and reuse them throughout your project.

Heatherheatherly answered 8/9, 2023 at 7:18 Comment(0)
H
0

Update: Reselect v5.1.0 now provides createSelector.withTypes<RootState>() which allows you to easily create a "pre-typed" version of createSelector:

import { createSelector } from 'reselect'

export interface RootState {
  todos: { id: number; completed: boolean }[]
  alerts: { id: number; read: boolean }[]
}

export const createAppSelector = createSelector.withTypes<RootState>()

const selectTodoIds = createAppSelector(
  [
    // Type of `state` is set to `RootState`, no need to manually set the type
    state => state.todos
  ],
  todos => todos.map(({ id }) => id)
)
Heatherheatherly answered 12/3, 2024 at 9:52 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.