Repository: reduxjs/redux-toolkit (ts)

Issue:
Export Type Definitions From Exported Packages
Would it be possible to export the type definitions for `reselect`? I'm trying to define types using the exported `Selector` type from `reselect` but it is not exported. Here's what I'd like to be able to do:
```typescript
import { Selector } from "@reduxjs/toolkit";

export type MySelector = Selector<{isActive: true}, boolean>
```

Pull request:
Feature/entity selectors
Adds `selectById` and exports the types from Reselect.

Fixes #421 .

Patch:
diff --git a/docs/api/createEntityAdapter.md b/docs/api/createEntityAdapter.md
index 373b8ef2..e13e7de8 100644
--- a/docs/api/createEntityAdapter.md
+++ b/docs/api/createEntityAdapter.md
@@ -176,6 +176,7 @@ export interface EntitySelectors<T, V> {
   selectEntities: (state: V) => Dictionary<T>
   selectAll: (state: V) => T[]
   selectTotal: (state: V) => number
+  selectById: (state: V, id: EntityId) => T | undefined
 }
 
 export interface EntityAdapter<T> extends EntityStateAdapter<T> {
@@ -247,12 +248,13 @@ const booksSlice = createSlice({
 
 ### Selector Functions
 
-The entity adapter will contain a `getSelectors()` function that returns a set of four selectors that know how to read the contents of an entity state object:
+The entity adapter will contain a `getSelectors()` function that returns a set of selectors that know how to read the contents of an entity state object:
 
 - `selectIds`: returns the `state.ids` array
 - `selectEntities`: returns the `state.entities` lookup table
 - `selectAll`: maps over the `state.ids` array, and returns an array of entities in the same order
 - `selectTotal`: returns the total number of entities being stored in this state
+- `selectById`: given the state and an entity ID, returns the entity with that ID or `undefined`
 
 Each selector function will be created using the `createSelector` function from Reselect, to enable memoizing calculation of the results.
 
diff --git a/etc/redux-toolkit.api.md b/etc/redux-toolkit.api.md
index 870e272c..c3272732 100644
--- a/etc/redux-toolkit.api.md
+++ b/etc/redux-toolkit.api.md
@@ -13,8 +13,12 @@ import { DeepPartial } from 'redux';
 import { Dispatch } from 'redux';
 import { Draft } from 'immer';
 import { Middleware } from 'redux';
+import { OutputParametricSelector } from 'reselect';
+import { OutputSelector } from 'reselect';
+import { ParametricSelector } from 'reselect';
 import { Reducer } from 'redux';
 import { ReducersMapObject } from 'redux';
+import { Selector } from 'reselect';
 import { Store } from 'redux';
 import { StoreEnhancer } from 'redux';
 import { ThunkAction } from 'redux-thunk';
@@ -212,6 +216,12 @@ export type IdSelector<T> = (model: T) => EntityId;
 // @public
 export function isPlain(val: any): boolean;
 
+export { OutputParametricSelector }
+
+export { OutputSelector }
+
+export { ParametricSelector }
+
 // @public
 export type PayloadAction<P = void, T extends string = string, M = never, E = never> = {
     payload: P;
@@ -240,6 +250,8 @@ export type PrepareAction<P> = ((...args: any[]) => {
     error: any;
 });
 
+export { Selector }
+
 // @public
 export interface SerializableStateInvariantMiddlewareOptions {
     getEntries?: (value: any) => [string, any][];
diff --git a/src/entities/models.ts b/src/entities/models.ts
index 30996a57..4a569617 100644
--- a/src/entities/models.ts
+++ b/src/entities/models.ts
@@ -125,6 +125,7 @@ export interface EntitySelectors<T, V> {
   selectEntities: (state: V) => Dictionary<T>
   selectAll: (state: V) => T[]
   selectTotal: (state: V) => number
+  selectById: (state: V, id: EntityId) => T | undefined
 }
 
 /**
diff --git a/src/entities/state_selectors.ts b/src/entities/state_selectors.ts
index 636792c1..d7370c87 100644
--- a/src/entities/state_selectors.ts
+++ b/src/entities/state_selectors.ts
@@ -1,5 +1,5 @@
 import { createSelector } from 'reselect'
-import { EntityState, EntitySelectors, Dictionary } from './models'
+import { EntityState, EntitySelectors, Dictionary, EntityId } from './models'
 
 export function createSelectorsFactory<T>() {
   function getSelectors(): EntitySelectors<T, EntityState<T>>
@@ -10,7 +10,9 @@ export function createSelectorsFactory<T>() {
     selectState?: (state: any) => EntityState<T>
   ): EntitySelectors<T, any> {
     const selectIds = (state: any) => state.ids
+
     const selectEntities = (state: EntityState<T>) => state.entities
+
     const selectAll = createSelector(
       selectIds,
       selectEntities,
@@ -18,6 +20,10 @@ export function createSelectorsFactory<T>() {
         ids.map((id: any) => (entities as any)[id])
     )
 
+    const selectId = (_: any, id: EntityId) => id
+
+    const selectById = (entities: Dictionary<T>, id: EntityId) => entities[id]
+
     const selectTotal = createSelector(selectIds, ids => ids.length)
 
     if (!selectState) {
@@ -25,15 +31,19 @@ export function createSelectorsFactory<T>() {
         selectIds,
         selectEntities,
         selectAll,
-        selectTotal
+        selectTotal,
+        selectById: createSelector(selectEntities, selectId, selectById)
       }
     }
 
+    const selectGlobalizedEntities = createSelector(selectState, selectEntities)
+
     return {
       selectIds: createSelector(selectState, selectIds),
-      selectEntities: createSelector(selectState, selectEntities),
+      selectEntities: selectGlobalizedEntities,
       selectAll: createSelector(selectState, selectAll),
-      selectTotal: createSelector(selectState, selectTotal)
+      selectTotal: createSelector(selectState, selectTotal),
+      selectById: createSelector(selectGlobalizedEntities, selectId, selectById)
     }
   }
 
diff --git a/src/index.ts b/src/index.ts
index c61b9095..670d542c 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,7 +1,13 @@
 import { enableES5 } from 'immer'
 export * from 'redux'
 export { default as createNextState, Draft } from 'immer'
-export { createSelector } from 'reselect'
+export {
+  createSelector,
+  Selector,
+  OutputParametricSelector,
+  OutputSelector,
+  ParametricSelector
+} from 'reselect'
 export { ThunkAction } from 'redux-thunk'
 
 // We deliberately enable Immer's ES5 support, on the grounds that


Test patch:
diff --git a/src/entities/state_selectors.test.ts b/src/entities/state_selectors.test.ts
index d0730383..7b8c11b7 100644
--- a/src/entities/state_selectors.test.ts
+++ b/src/entities/state_selectors.test.ts
@@ -57,6 +57,13 @@ describe('Entity State Selectors', () => {
 
       expect(total).toEqual(3)
     })
+
+    it('should create a selector for selecting a single item by ID', () => {
+      const first = selectors.selectById(state, AClockworkOrange.id)
+      expect(first).toBe(AClockworkOrange)
+      const second = selectors.selectById(state, AnimalFarm.id)
+      expect(second).toBe(AnimalFarm)
+    })
   })
 
   describe('Uncomposed Selectors', () => {
@@ -110,5 +117,12 @@ describe('Entity State Selectors', () => {
 
       expect(total).toEqual(3)
     })
+
+    it('should create a selector for selecting a single item by ID', () => {
+      const first = selectors.selectById(state, AClockworkOrange.id)
+      expect(first).toBe(AClockworkOrange)
+      const second = selectors.selectById(state, AnimalFarm.id)
+      expect(second).toBe(AnimalFarm)
+    })
   })
 })