Skip to content

Commit

Permalink
Merge branch 'argsMemoize' into memoizeOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
aryaemami59 committed Oct 4, 2023
2 parents 656dced + 8ad544e commit c991f9f
Show file tree
Hide file tree
Showing 9 changed files with 1,377 additions and 264 deletions.
246 changes: 162 additions & 84 deletions src/index.ts

Large diffs are not rendered by default.

52 changes: 42 additions & 10 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ export type { MergeParameters } from './versionedTypes'
*
*/

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

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

/** Represents the actual selectors generated by `createSelector`.
/**
* Represents the actual selectors generated by `createSelector`.
* The selector is:
* - "a function that takes this state + params and returns a result"
* - plus the attached additional fields
*/
export type OutputSelector<
S extends SelectorArray,
Result,
Combiner extends UnknownFunction,
Combiner extends AnyFunction,
Params extends readonly any[] = never, // MergeParameters<S>
Keys = {}
> = Selector<GetStateFromSelectors<S>, Result, Params> &
OutputSelectorFields<Combiner, Keys>

/** A selector that is assumed to have one additional argument, such as
/**
* A selector that is assumed to have one additional argument, such as
* the props from a React component
*/
export type ParametricSelector<State, Props, Result> = Selector<
Expand All @@ -69,9 +72,10 @@ export type OutputParametricSelector<
State,
Props,
Result,
Combiner extends UnknownFunction,
Combiner extends AnyFunction,
Keys = {}
> = ParametricSelector<State, Props, Result> & OutputSelectorFields<Combiner, Keys>
> = ParametricSelector<State, Props, Result> &
OutputSelectorFields<Combiner, Keys>

/** An array of input selectors */
export type SelectorArray = ReadonlyArray<Selector>
Expand Down Expand Up @@ -106,10 +110,37 @@ export type GetParamsFromSelectors<
*/

/** Any function with arguments */
export type UnknownFunction = (...args: any[]) => any
export type AnyFunction = (...args: any[]) => any
/** Any function with unknown arguments */
export type UnknownFunction = (...args: unknown[]) => unknown
/** Any Memoizer function. A memoizer is a function that accepts another function and returns it. */
export type UnknownMemoizer<Func extends UnknownFunction = UnknownFunction> = (
func: Func,
...options: any[]
) => Func

/**
* Omit any index signatures from the given object type, leaving only explicitly defined properties.
* Source: https://stackoverflow.com/questions/51465182/how-to-remove-index-signature-using-mapped-types/68261113#68261113
* This is mainly used to remove explicit `any`s from the return type of some memoizers. e.g: `microMemoize`
*/
export type OmitIndexSignature<ObjectType> = {
[KeyType in keyof ObjectType as {} extends Record<KeyType, unknown>
? never
: KeyType]: ObjectType[KeyType]
}

/** Extracts memoize options from the parameters of a memoizer function. */
export type MemoizeOptsFromParams<MemoizeFunction extends UnknownMemoizer> =
| DropFirst<Parameters<MemoizeFunction>>[0]
| DropFirst<Parameters<MemoizeFunction>>

/** Extract the extra properties that are attached to the return value of a memoizer. e.g.: clearCache */
export type ExtractMemoizerFields<T extends UnknownMemoizer> =
OmitIndexSignature<ReturnType<T>>

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

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

/** The infamous "convert a union type to an intersection type" hack
/**
* The infamous "convert a union type to an intersection type" hack
* Source: https://github.com/sindresorhus/type-fest/blob/main/source/union-to-intersection.d.ts
* Reference: https://github.com/microsoft/TypeScript/issues/29594
*/
Expand Down
10 changes: 5 additions & 5 deletions src/versionedTypes/ts46-mergeParameters.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type {
UnknownFunction,
AnyFunction,
Expand,
TuplifyUnion,
Has,
IsTuple,
List,
IsTuple
TuplifyUnion
} from '../types'

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

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

Expand Down
56 changes: 56 additions & 0 deletions test/reselect.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { createSelector } from '@reduxjs/toolkit'
import { bench } from 'vitest'
import { autotrackMemoize } from '../src/autotrackMemoize/autotrackMemoize'
import { weakMapMemoize } from '../src/weakMapMemoize'

describe('bench', () => {
interface State {
todos: {
id: number
completed: boolean
}[]
}
const state: State = {
todos: [
{ id: 0, completed: false },
{ id: 1, completed: false }
]
}
bench(
'selectorDefault',
() => {
const selectorDefault = createSelector(
(state: State) => state.todos,
todos => todos.map(t => t.id)
)
selectorDefault(state)
},
{ iterations: 500 }
)

bench(
'selectorAutotrack',
() => {
const selectorAutotrack = createSelector(
(state: State) => state.todos,
todos => todos.map(t => t.id),
{ memoize: autotrackMemoize }
)
selectorAutotrack(state)
},
{ iterations: 500 }
)

bench(
'selectorWeakMap',
() => {
const selectorWeakMap = createSelector(
(state: State) => state.todos,
todos => todos.map(t => t.id),
{ memoize: weakMapMemoize }
)
selectorWeakMap(state)
},
{ iterations: 500 }
)
})
Loading

0 comments on commit c991f9f

Please sign in to comment.