SuperManEver
SuperManEver

Reputation: 2362

how to use selectors in redux app with TypeScript?

I try to use selectors from reselect library in my Redux app.

my selectors file looks:

import { createSelector } from 'reselect'

const postsSelectors = state => state.global.posts.allPostse;

export const getPosts = createSelector(
  [ postsSelectors ],
  (posts) => posts
);

and then I try to use in my component, like this:

const mapStateToProps = (state) => ({
  posts: getPosts(state),
});

When I try to compile all of this, I got this error:

enter image description here

I'm guessing that with how I declare types for props, which currently looks like that:

interface Props{
 posts(state: any): any
 loadStories(): void;
};

Help me please resolve this issue. Thanks in advance.

Upvotes: 13

Views: 21974

Answers (3)

Oleksandr Danylchenko
Oleksandr Danylchenko

Reputation: 698

In the [email protected] generics typing signature slightly changed if you're using the typescript@≥4.2. So now it needs to be specified as: createSelector<Selectors extends SelectorArray, Result>

<Selectors extends SelectorArray, Result>

export type Selector<
  // The state can be anything
  State = any,
  // The result will be inferred
  Result = unknown,
  // There are either 0 params, or N params
  Params extends never | readonly any[] = any[]
  // If there are 0 params, type the function as just State in, Result out.
  // Otherwise, type it as State + Params in, Result out.
> = [Params] extends [never]
  ? (state: State) => Result
  : (state: State, ...params: Params) => Result

export type SelectorArray = ReadonlyArray<Selector>

Example:

// get all posts
export const selectPosts = (state: TState): TPostsState => state.posts;

// get new posts
export const selectNewPosts = createSelector<
  [Selector<TState, TPostsState>],
  TPostData[]
>(
    selectPosts,
    (posts) => posts.filter(({ type }) => type === 'new'),
  );

But in general, for the newer TS, you should not specify types manually now, as they will be automatically inferred.

If you're getting TS4023: Exported variable 'X' has or is using the name '$CombinedState' from external module, please refer to the Stackoverflow answer https://stackoverflow.com/a/43901135/10963661 or try to set "declaration": false compiler option in the tsconfig.json file.

Upvotes: 3

Ivan Stinsky
Ivan Stinsky

Reputation: 121

An example with more types:

  1. Describe types
type TPostData = {
  type: string;
};

type TPostsState = TPostData[];

type TState = {
  posts: TPostsState;
};
  1. Create selectors
// get all posts
export const selectPosts = (state: TState): TPostsState => state.posts;

// get new posts
export const selectNewPosts = createSelector<
  TState,
  TPostsState,
  TPostData[]>(
    selectPosts,
    (posts) => posts.filter(({ type }) => type === 'new'),
  );

Result: You've got all posts with type parameter 'new'.

Upvotes: 12

Radio-
Radio-

Reputation: 3171

TypeScript is not expecting an array for the first argument. Just pass in your selector functions as arguments to createSelector, as in

export const getPosts = createSelector(
  postsSelectors,
  (posts) => posts
);

Upvotes: 6

Related Questions