Skip to content

Commit c991f9f

Browse files
committed
Merge branch 'argsMemoize' into memoizeOptions
2 parents 656dced + 8ad544e commit c991f9f

File tree

9 files changed

+1377
-264
lines changed

9 files changed

+1377
-264
lines changed

src/index.ts

Lines changed: 162 additions & 84 deletions
Large diffs are not rendered by default.

src/types.ts

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ export type { MergeParameters } from './versionedTypes'
77
*
88
*/
99

10-
/** A standard selector function, which takes three generic type arguments:
10+
/**
11+
* A standard selector function, which takes three generic type arguments:
1112
* @param State The first value, often a Redux root state object
1213
* @param Result The final result returned by the selector
1314
* @param Params All additional arguments passed into the selector
@@ -26,7 +27,7 @@ export type Selector<
2627
: (state: State, ...params: Params) => Result
2728

2829
/** Selectors generated by Reselect have several additional fields attached: */
29-
export interface OutputSelectorFields<Combiner extends UnknownFunction, Keys> {
30+
export interface OutputSelectorFields<Combiner extends AnyFunction, Keys> {
3031
/** The final function passed to `createSelector` */
3132
resultFunc: Combiner
3233
/** The same function, memoized */
@@ -41,21 +42,23 @@ export interface OutputSelectorFields<Combiner extends UnknownFunction, Keys> {
4142
resetRecomputations: () => number
4243
}
4344

44-
/** Represents the actual selectors generated by `createSelector`.
45+
/**
46+
* Represents the actual selectors generated by `createSelector`.
4547
* The selector is:
4648
* - "a function that takes this state + params and returns a result"
4749
* - plus the attached additional fields
4850
*/
4951
export type OutputSelector<
5052
S extends SelectorArray,
5153
Result,
52-
Combiner extends UnknownFunction,
54+
Combiner extends AnyFunction,
5355
Params extends readonly any[] = never, // MergeParameters<S>
5456
Keys = {}
5557
> = Selector<GetStateFromSelectors<S>, Result, Params> &
5658
OutputSelectorFields<Combiner, Keys>
5759

58-
/** A selector that is assumed to have one additional argument, such as
60+
/**
61+
* A selector that is assumed to have one additional argument, such as
5962
* the props from a React component
6063
*/
6164
export type ParametricSelector<State, Props, Result> = Selector<
@@ -69,9 +72,10 @@ export type OutputParametricSelector<
6972
State,
7073
Props,
7174
Result,
72-
Combiner extends UnknownFunction,
75+
Combiner extends AnyFunction,
7376
Keys = {}
74-
> = ParametricSelector<State, Props, Result> & OutputSelectorFields<Combiner, Keys>
77+
> = ParametricSelector<State, Props, Result> &
78+
OutputSelectorFields<Combiner, Keys>
7579

7680
/** An array of input selectors */
7781
export type SelectorArray = ReadonlyArray<Selector>
@@ -106,10 +110,37 @@ export type GetParamsFromSelectors<
106110
*/
107111

108112
/** Any function with arguments */
109-
export type UnknownFunction = (...args: any[]) => any
113+
export type AnyFunction = (...args: any[]) => any
114+
/** Any function with unknown arguments */
115+
export type UnknownFunction = (...args: unknown[]) => unknown
116+
/** Any Memoizer function. A memoizer is a function that accepts another function and returns it. */
117+
export type UnknownMemoizer<Func extends UnknownFunction = UnknownFunction> = (
118+
func: Func,
119+
...options: any[]
120+
) => Func
121+
122+
/**
123+
* Omit any index signatures from the given object type, leaving only explicitly defined properties.
124+
* Source: https://stackoverflow.com/questions/51465182/how-to-remove-index-signature-using-mapped-types/68261113#68261113
125+
* This is mainly used to remove explicit `any`s from the return type of some memoizers. e.g: `microMemoize`
126+
*/
127+
export type OmitIndexSignature<ObjectType> = {
128+
[KeyType in keyof ObjectType as {} extends Record<KeyType, unknown>
129+
? never
130+
: KeyType]: ObjectType[KeyType]
131+
}
132+
133+
/** Extracts memoize options from the parameters of a memoizer function. */
134+
export type MemoizeOptsFromParams<MemoizeFunction extends UnknownMemoizer> =
135+
| DropFirst<Parameters<MemoizeFunction>>[0]
136+
| DropFirst<Parameters<MemoizeFunction>>
137+
138+
/** Extract the extra properties that are attached to the return value of a memoizer. e.g.: clearCache */
139+
export type ExtractMemoizerFields<T extends UnknownMemoizer> =
140+
OmitIndexSignature<ReturnType<T>>
110141

111142
/** Extract the return type from all functions as a tuple */
112-
export type ExtractReturnType<T extends readonly UnknownFunction[]> = {
143+
export type ExtractReturnType<T extends readonly AnyFunction[]> = {
113144
[index in keyof T]: T[index] extends T[number] ? ReturnType<T[index]> : never
114145
}
115146

@@ -135,7 +166,8 @@ export type Has<U, U1> = [U1] extends [U] ? 1 : 0
135166
*
136167
*/
137168

138-
/** The infamous "convert a union type to an intersection type" hack
169+
/**
170+
* The infamous "convert a union type to an intersection type" hack
139171
* Source: https://github.com/sindresorhus/type-fest/blob/main/source/union-to-intersection.d.ts
140172
* Reference: https://github.com/microsoft/TypeScript/issues/29594
141173
*/

src/versionedTypes/ts46-mergeParameters.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type {
2-
UnknownFunction,
2+
AnyFunction,
33
Expand,
4-
TuplifyUnion,
54
Has,
5+
IsTuple,
66
List,
7-
IsTuple
7+
TuplifyUnion
88
} from '../types'
99

1010
/** Given a set of input selectors, extracts the intersected parameters to determine
@@ -13,7 +13,7 @@ import type {
1313
*/
1414
export type MergeParameters<
1515
// The actual array of input selectors
16-
T extends readonly UnknownFunction[],
16+
T extends readonly AnyFunction[],
1717
// Given those selectors, we do several transformations on the types in sequence:
1818
// 1) Extract "the type of parameters" for each input selector, so that we now have
1919
// a tuple of all those parameters
@@ -77,7 +77,7 @@ type EmptyObject = {
7777
type IgnoreInvalidIntersections<T> = T extends EmptyObject ? never : T
7878

7979
/** Extract the parameters from all functions as a tuple */
80-
export type ExtractParams<T extends readonly UnknownFunction[]> = {
80+
export type ExtractParams<T extends readonly AnyFunction[]> = {
8181
[index in keyof T]: T[index] extends T[number] ? Parameters<T[index]> : never
8282
}
8383

test/reselect.bench.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import { createSelector } from '@reduxjs/toolkit'
2+
import { bench } from 'vitest'
3+
import { autotrackMemoize } from '../src/autotrackMemoize/autotrackMemoize'
4+
import { weakMapMemoize } from '../src/weakMapMemoize'
5+
6+
describe('bench', () => {
7+
interface State {
8+
todos: {
9+
id: number
10+
completed: boolean
11+
}[]
12+
}
13+
const state: State = {
14+
todos: [
15+
{ id: 0, completed: false },
16+
{ id: 1, completed: false }
17+
]
18+
}
19+
bench(
20+
'selectorDefault',
21+
() => {
22+
const selectorDefault = createSelector(
23+
(state: State) => state.todos,
24+
todos => todos.map(t => t.id)
25+
)
26+
selectorDefault(state)
27+
},
28+
{ iterations: 500 }
29+
)
30+
31+
bench(
32+
'selectorAutotrack',
33+
() => {
34+
const selectorAutotrack = createSelector(
35+
(state: State) => state.todos,
36+
todos => todos.map(t => t.id),
37+
{ memoize: autotrackMemoize }
38+
)
39+
selectorAutotrack(state)
40+
},
41+
{ iterations: 500 }
42+
)
43+
44+
bench(
45+
'selectorWeakMap',
46+
() => {
47+
const selectorWeakMap = createSelector(
48+
(state: State) => state.todos,
49+
todos => todos.map(t => t.id),
50+
{ memoize: weakMapMemoize }
51+
)
52+
selectorWeakMap(state)
53+
},
54+
{ iterations: 500 }
55+
)
56+
})

0 commit comments

Comments
 (0)