From 4e08e6d4ddbcab64b6f9d25fab5a0c2623ad852c Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Thu, 21 Mar 2024 12:56:16 -0500 Subject: [PATCH 1/4] Remove `trailingComma` option from `.prettierrc.json` --- .prettierrc.json | 1 - 1 file changed, 1 deletion(-) diff --git a/.prettierrc.json b/.prettierrc.json index e7bcdcc73..60cbbf239 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -2,6 +2,5 @@ "semi": false, "singleQuote": true, "tabWidth": 2, - "trailingComma": "none", "arrowParens": "avoid" } From a2153bb07b5486c7261579bd16a7a2cfde3c8e9d Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Thu, 21 Mar 2024 13:02:09 -0500 Subject: [PATCH 2/4] Remove `comma-dangle` rule from `.eslintrc` --- .eslintrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 3f6d01cb9..fd5873c30 100644 --- a/.eslintrc +++ b/.eslintrc @@ -10,7 +10,6 @@ }, "rules": { "array-bracket-spacing": [0], - "comma-dangle": [2, "never"], "eol-last": 2, "no-multiple-empty-lines": 2, "object-curly-spacing": [2, "always"], From 3a8fe3c70905effeff5a24c8e065f029dd38fd61 Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Thu, 21 Mar 2024 12:55:53 -0500 Subject: [PATCH 3/4] Bump Prettier to version 3.3.3 --- package.json | 2 +- yarn.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/package.json b/package.json index ed525b1e5..f3c87d3b3 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "memoize-one": "^6.0.0", "micro-memoize": "^4.0.9", "netlify-plugin-cache": "^1.0.3", - "prettier": "^2.7.1", + "prettier": "^3.3.3", "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^9.0.4", diff --git a/yarn.lock b/yarn.lock index 56f2111ff..e65e8881f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3934,12 +3934,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.7.1": - version: 2.8.8 - resolution: "prettier@npm:2.8.8" +"prettier@npm:^3.3.3": + version: 3.3.3 + resolution: "prettier@npm:3.3.3" bin: - prettier: bin-prettier.js - checksum: 10/00cdb6ab0281f98306cd1847425c24cbaaa48a5ff03633945ab4c701901b8e96ad558eb0777364ffc312f437af9b5a07d0f45346266e8245beaf6247b9c62b24 + prettier: bin/prettier.cjs + checksum: 10/5beac1f30b5b40162532b8e2f7c3a4eb650910a2695e9c8512a62ffdc09dae93190c29db9107fa7f26d1b6c71aad3628ecb9b5de1ecb0911191099be109434d7 languageName: node linkType: hard @@ -4219,7 +4219,7 @@ __metadata: memoize-one: "npm:^6.0.0" micro-memoize: "npm:^4.0.9" netlify-plugin-cache: "npm:^1.0.3" - prettier: "npm:^2.7.1" + prettier: "npm:^3.3.3" react: "npm:^18.2.0" react-dom: "npm:^18.2.0" react-redux: "npm:^9.0.4" From 186000e1917a66891ffb071f2c3a8f4714aec811 Mon Sep 17 00:00:00 2001 From: Arya Emami Date: Thu, 21 Mar 2024 12:57:13 -0500 Subject: [PATCH 4/4] Format all files --- .github/workflows/build-and-test-types.yml | 2 +- CHANGELOG.md | 18 +- CREDITS.md | 10 +- README.md | 12 +- codecov.yml | 8 +- docs/examples/FAQ/createCurriedSelector.ts | 2 +- .../FAQ/createParametricSelectorHook.ts | 16 +- docs/examples/FAQ/currySelector.ts | 8 +- docs/examples/FAQ/howToTest.test.ts | 10 +- docs/examples/FAQ/identity.ts | 2 +- docs/examples/FAQ/selectorRecomputing.ts | 8 +- docs/examples/basicUsage.ts | 10 +- .../createSelector/annotateResultFunction.ts | 2 +- .../createSelector/createAppSelector.ts | 12 +- docs/examples/createSelector/withTypes.ts | 4 +- .../createStructuredSelector/MyComponent.tsx | 2 +- .../createStructuredSelector/modernUseCase.ts | 10 +- .../createStructuredSelector/withTypes.ts | 2 +- .../identityFunctionCheck.ts | 2 +- .../inputStabilityCheck.ts | 4 +- .../fallbackToEmptyArray.ts | 2 +- .../firstPattern.ts | 2 +- .../lruMemoize/usingWithCreateSelector.ts | 8 +- .../usingWithCreateSelectorCreator.ts | 8 +- .../usingWithCreateSelector.ts | 2 +- .../usingWithCreateSelectorCreator.ts | 4 +- .../weakMapMemoize/cacheSizeProblem.ts | 8 +- .../weakMapMemoize/cacheSizeSolution.ts | 10 +- docs/examples/weakMapMemoize/setMaxSize.ts | 8 +- .../weakMapMemoize/usingWithCreateSelector.ts | 10 +- .../usingWithCreateSelectorCreator.ts | 10 +- docs/examples/weakMapMemoize/withUseMemo.tsx | 4 +- src/autotrackMemoize/autotrackMemoize.ts | 2 +- src/autotrackMemoize/autotracking.ts | 8 +- src/autotrackMemoize/proxy.ts | 14 +- src/autotrackMemoize/tracking.ts | 4 +- src/autotrackMemoize/utils.ts | 2 +- src/createSelectorCreator.ts | 136 +- src/createStructuredSelector.ts | 27 +- src/devModeChecks/identityFunctionCheck.ts | 4 +- src/devModeChecks/inputStabilityCheck.ts | 6 +- src/devModeChecks/setGlobalDevModeChecks.ts | 4 +- src/index.ts | 4 +- src/lruMemoize.ts | 12 +- src/types.ts | 55 +- src/utils.ts | 24 +- src/versionedTypes/ts47-mergeParameters.ts | 20 +- src/weakMapMemoize.ts | 6 +- test/autotrackMemoize.spec.ts | 32 +- test/benchmarks/orderOfExecution.bench.ts | 426 +-- test/benchmarks/reselect.bench.ts | 80 +- test/benchmarks/resultEqualityCheck.bench.ts | 52 +- test/benchmarks/weakMapMemoize.bench.ts | 992 +++---- test/computationComparisons.spec.tsx | 48 +- test/createSelector.withTypes.test.ts | 2 +- test/createStructuredSelector.spec.ts | 70 +- ...createStructuredSelector.withTypes.test.ts | 4 +- test/examples.test.ts | 46 +- test/identityFunctionCheck.test.ts | 40 +- test/inputStabilityCheck.spec.ts | 44 +- test/lruMemoize.test.ts | 92 +- test/perfComparisons.spec.ts | 66 +- test/reselect.spec.ts | 246 +- test/selectorUtils.spec.ts | 6 +- test/setup.vitest.ts | 4 +- test/testUtils.ts | 1092 ++++---- test/weakmapMemoize.spec.ts | 56 +- tsup.config.ts | 34 +- type-tests/argsMemoize.test-d.ts | 1786 ++++++------- type-tests/createSelector.withTypes.test-d.ts | 18 +- type-tests/createSelectorCreator.test-d.ts | 116 +- type-tests/createStructuredSelector.test-d.ts | 545 ++-- ...eateStructuredSelector.withTypes.test-d.ts | 108 +- type-tests/deepNesting.test-d.ts | 640 ++--- type-tests/tsconfig.json | 34 +- typescript_test/argsMemoize.typetest.ts | 2322 +++++++++-------- typescript_test/test.ts | 341 ++- typescript_test/typesTestUtils.ts | 57 +- vitest.config.mts | 6 +- website/babel.config.js | 2 +- website/compileExamples.ts | 58 +- website/docs/FAQ.mdx | 100 +- website/docs/api/createSelector.mdx | 40 +- website/docs/api/createSelectorCreator.mdx | 14 +- website/docs/api/createStructuredSelector.mdx | 36 +- .../api/development-only-stability-checks.mdx | 14 +- website/docs/api/lruMemoize.mdx | 32 +- .../docs/api/unstable_autotrackMemoize.mdx | 12 +- website/docs/api/weakMapMemoize.mdx | 94 +- website/docs/introduction/getting-started.mdx | 37 +- .../introduction/how-does-reselect-work.mdx | 6 +- website/docs/usage/best-practices.mdx | 4 +- website/docs/usage/common-mistakes.mdx | 4 +- .../usage/handling-empty-array-results.mdx | 4 +- website/docusaurus.config.ts | 46 +- website/insertCodeExamples.ts | 14 +- website/monokaiTheme.js | 42 +- website/sidebars.ts | 20 +- website/src/components/ExternalLinks.tsx | 2 +- .../src/components/HomepageFeatures/index.tsx | 164 +- website/src/components/InternalLinks.tsx | 2 +- website/src/components/PackageManagerTabs.tsx | 2 +- website/src/css/custom.css | 10 +- 103 files changed, 5363 insertions(+), 5349 deletions(-) diff --git a/.github/workflows/build-and-test-types.yml b/.github/workflows/build-and-test-types.yml index 6f15ae874..88fffc7b4 100644 --- a/.github/workflows/build-and-test-types.yml +++ b/.github/workflows/build-and-test-types.yml @@ -131,7 +131,7 @@ jobs: 'node-standard', 'node-esm', 'react-native', - 'expo' + 'expo', ] steps: - name: Checkout repo diff --git a/CHANGELOG.md b/CHANGELOG.md index da2a48a7a..edb213724 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,7 +40,7 @@ const mySelector = createSelector( values => { console.log('calling..') return values.reduce((acc, val) => acc + val, 0) - } + }, ) var createSelector = require('./dist/reselect.js').createSelector @@ -50,7 +50,7 @@ const mySelector = createSelector( values => { console.log('calling..') return values.reduce((acc, val) => acc + val, 0) - } + }, ) var state1 = { values: [1, 2, 3, 4, 5, 6, 7, 8, 9] } @@ -189,8 +189,8 @@ const structuredSelector = createSelector( (a, b, c) => ({ a, b, - c - }) + c, + }), ) ``` @@ -202,7 +202,7 @@ const mySelectorB = state => state.b const structuredSelector = createStructuredSelector({ x: mySelectorA, - y: mySelectorB + y: mySelectorB, }) const result = structuredSelector({ a: 1, b: 2 }) // will produce {x: 1, y: 2} @@ -256,7 +256,7 @@ Selector creators can receive a variadic number of dependencies as well as an ar ```js const selector = createSelector( [state => state.a, state => state.b], - (a, b) => a * b + (a, b) => a * b, ) ``` @@ -266,7 +266,7 @@ const selector = createSelector( const selector = createSelector( state => state.a, state => state.b, - (a, b) => a * b + (a, b) => a * b, ) ``` @@ -279,7 +279,7 @@ const selector = createSelector( state => state.a, (state, props) => state.b * props.c, (_, props) => props.d, - (a, bc, d) => a + bc + d + (a, bc, d) => a + bc + d, ) ``` @@ -297,7 +297,7 @@ const selector = customSelectorCreator( (a, b) => { called++ return a + b - } + }, ) assert.equal(selector({ a: 1, b: 2 }), 3) assert.equal(selector({ a: 1, b: 2 }), 3) diff --git a/CREDITS.md b/CREDITS.md index 435c64a67..cfce8befd 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -1,12 +1,12 @@ # CREDITS -* Based on a proposal for Redux by [Robert Binna](https://github.com/speedskater) and Philip Spitzlinger. +- Based on a proposal for Redux by [Robert Binna](https://github.com/speedskater) and Philip Spitzlinger. Lots of the basic structure of the code is thanks to this. -* Refactored into reselect library by Martijn Faassen and Lee Bannard +- Refactored into reselect library by Martijn Faassen and Lee Bannard at the React Europe Hackathon 2015. Also added tests. -* Contributors: Lee Bannard, Martijn Faassen, Robert Binna, Alex +- Contributors: Lee Bannard, Martijn Faassen, Robert Binna, Alex Guerra, ryanatkn, Adam Royle, Christian Schuhmann, Jason Huang, Daniel Barreto, Mihail Diordiev, Daniela Borges, Philip Spitzlinger, C. T. Lin, SpainTrain, Mark Dalgleish, Brian Ng, 长天之云, Michael Lancaster, @@ -19,6 +19,6 @@ Whien, Sergei Egorov, Jim Bolla, Carl Bernardo, Daniel Lytkin, John Haley, alex3165, -* Inspired by getters in Nuclear.js and subscriptions in re-frame. +- Inspired by getters in Nuclear.js and subscriptions in re-frame. -* Special thanks to [David Edmonson](https://github.com/threehams) for maintaining the Typescript type definitions. +- Special thanks to [David Edmonson](https://github.com/threehams) for maintaining the Typescript type definitions. diff --git a/README.md b/README.md index 877066feb..54dc9487a 100644 --- a/README.md +++ b/README.md @@ -66,12 +66,12 @@ interface RootState { const state: RootState = { todos: [ { id: 0, completed: false }, - { id: 1, completed: true } + { id: 1, completed: true }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } const selectCompletedTodos = (state: RootState) => { @@ -88,7 +88,7 @@ const memoizedSelectCompletedTodos = createSelector( todos => { console.log('memoized selector ran') return todos.filter(todo => todo.completed === true) - } + }, ) memoizedSelectCompletedTodos(state) // memoized selector ran @@ -98,7 +98,7 @@ memoizedSelectCompletedTodos(state) console.log(selectCompletedTodos(state) === selectCompletedTodos(state)) //=> false console.log( - memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state) + memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state), ) //=> true ``` @@ -121,7 +121,7 @@ The below example serves as a visual aid: ```ts const outputSelector = createSelector( [inputSelector1, inputSelector2, inputSelector3], // synonymous with `dependencies`. - resultFunc // Result function + resultFunc, // Result function ) ``` diff --git a/codecov.yml b/codecov.yml index c4f00f6f5..0b13efa10 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,6 +1,6 @@ comment: - layout: "reach, diff, flags, files" + layout: 'reach, diff, flags, files' behavior: default - require_changes: false # if true: only post the comment if coverage changes - require_base: no # [yes :: must have a base report to post] - require_head: no # [yes :: must have a head report to post] + require_changes: false # if true: only post the comment if coverage changes + require_base: no # [yes :: must have a base report to post] + require_head: no # [yes :: must have a head report to post] diff --git a/docs/examples/FAQ/createCurriedSelector.ts b/docs/examples/FAQ/createCurriedSelector.ts index b0d575b1e..24510f985 100644 --- a/docs/examples/FAQ/createCurriedSelector.ts +++ b/docs/examples/FAQ/createCurriedSelector.ts @@ -6,7 +6,7 @@ export const createCurriedSelector = < InputSelectors extends SelectorArray, Result, OverrideMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - OverrideArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + OverrideArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, >( ...args: Parameters< typeof createSelector< diff --git a/docs/examples/FAQ/createParametricSelectorHook.ts b/docs/examples/FAQ/createParametricSelectorHook.ts index f88bfce50..04717803f 100644 --- a/docs/examples/FAQ/createParametricSelectorHook.ts +++ b/docs/examples/FAQ/createParametricSelectorHook.ts @@ -17,31 +17,31 @@ const state: RootState = { id: 0, completed: false, title: 'Figure out if plants are really plotting world domination.', - description: 'They may be.' + description: 'They may be.', }, { id: 1, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } + description: 'Just do it', + }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } const selectTodoById = createSelector( [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id) + (todos, id) => todos.find(todo => todo.id === id), ) export const createParametricSelectorHook = < Result, - Params extends readonly unknown[] + Params extends readonly unknown[], >( - selector: (state: RootState, ...params: Params) => Result + selector: (state: RootState, ...params: Params) => Result, ) => { return (...args: Params) => { return useSelector((state: RootState) => selector(state, ...args)) diff --git a/docs/examples/FAQ/currySelector.ts b/docs/examples/FAQ/currySelector.ts index 670cfff51..a73ba9480 100644 --- a/docs/examples/FAQ/currySelector.ts +++ b/docs/examples/FAQ/currySelector.ts @@ -5,9 +5,9 @@ export const currySelector = < State, Result, Params extends readonly any[], - AdditionalFields + AdditionalFields, >( - selector: ((state: State, ...args: Params) => Result) & AdditionalFields + selector: ((state: State, ...args: Params) => Result) & AdditionalFields, ) => { const curriedSelector = (...args: Params) => { return (state: State) => { @@ -20,6 +20,6 @@ export const currySelector = < const selectTodoByIdCurried = currySelector( createSelector( [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id) - ) + (todos, id) => todos.find(todo => todo.id === id), + ), ) diff --git a/docs/examples/FAQ/howToTest.test.ts b/docs/examples/FAQ/howToTest.test.ts index d57bf1eff..2cf9304e5 100644 --- a/docs/examples/FAQ/howToTest.test.ts +++ b/docs/examples/FAQ/howToTest.test.ts @@ -9,19 +9,19 @@ interface RootState { const state: RootState = { todos: [ { id: 0, completed: false }, - { id: 1, completed: true } + { id: 1, completed: true }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } // With `Vitest` or `Jest` test('selector unit test', () => { const selectTodoIds = createSelector( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const firstResult = selectTodoIds(state) const secondResult = selectTodoIds(state) @@ -42,7 +42,7 @@ test('selector unit test', () => { test('selector unit test', () => { const selectTodoIds = createSelector( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const firstResult = selectTodoIds(state) const secondResult = selectTodoIds(state) diff --git a/docs/examples/FAQ/identity.ts b/docs/examples/FAQ/identity.ts index 13db560af..e1a2825bf 100644 --- a/docs/examples/FAQ/identity.ts +++ b/docs/examples/FAQ/identity.ts @@ -4,5 +4,5 @@ const identity = any>(func: Func) => func const createNonMemoizedSelector = createSelectorCreator({ memoize: identity, - argsMemoize: identity + argsMemoize: identity, }) diff --git a/docs/examples/FAQ/selectorRecomputing.ts b/docs/examples/FAQ/selectorRecomputing.ts index b61135ce6..56c676814 100644 --- a/docs/examples/FAQ/selectorRecomputing.ts +++ b/docs/examples/FAQ/selectorRecomputing.ts @@ -8,7 +8,7 @@ export interface RootState { const selectAlertsByType = createSelector( [ (state: RootState) => state.alerts, - (state: RootState, type: string) => type + (state: RootState, type: string) => type, ], (alerts, type) => alerts.filter(todo => todo.type === type), { @@ -20,7 +20,7 @@ const selectAlertsByType = createSelector( console.log('Changed argument:', a, 'to', b) } return a === b - } - } - } + }, + }, + }, ) diff --git a/docs/examples/basicUsage.ts b/docs/examples/basicUsage.ts index d3286d886..b68e70a50 100644 --- a/docs/examples/basicUsage.ts +++ b/docs/examples/basicUsage.ts @@ -8,12 +8,12 @@ interface RootState { const state: RootState = { todos: [ { id: 0, completed: false }, - { id: 1, completed: true } + { id: 1, completed: true }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } const selectCompletedTodos = (state: RootState) => { @@ -30,7 +30,7 @@ const memoizedSelectCompletedTodos = createSelector( todos => { console.log('memoized selector ran') return todos.filter(todo => todo.completed === true) - } + }, ) memoizedSelectCompletedTodos(state) // memoized selector ran @@ -40,5 +40,5 @@ memoizedSelectCompletedTodos(state) console.log(selectCompletedTodos(state) === selectCompletedTodos(state)) //=> false console.log( - memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state) + memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state), ) //=> true diff --git a/docs/examples/createSelector/annotateResultFunction.ts b/docs/examples/createSelector/annotateResultFunction.ts index 62b5cdc14..90fe10a1d 100644 --- a/docs/examples/createSelector/annotateResultFunction.ts +++ b/docs/examples/createSelector/annotateResultFunction.ts @@ -23,6 +23,6 @@ const selectTodoIds = createAppSelector( // ❌ Known limitation: Parameter types are not inferred in this scenario // so you will have to manually annotate them. // highlight-start - (todos: Todo[]) => todos.map(({ id }) => id) + (todos: Todo[]) => todos.map(({ id }) => id), // highlight-end ) diff --git a/docs/examples/createSelector/createAppSelector.ts b/docs/examples/createSelector/createAppSelector.ts index 52ac14863..c24831159 100644 --- a/docs/examples/createSelector/createAppSelector.ts +++ b/docs/examples/createSelector/createAppSelector.ts @@ -13,24 +13,24 @@ export const createAppSelector = createSelectorCreator({ memoizeOptions: { maxSize: 10, equalityCheck: shallowEqual, - resultEqualityCheck: shallowEqual + resultEqualityCheck: shallowEqual, }, argsMemoizeOptions: { isEqual: shallowEqual, - maxSize: 10 + maxSize: 10, }, devModeChecks: { identityFunctionCheck: 'never', - inputStabilityCheck: 'always' - } + inputStabilityCheck: 'always', + }, }).withTypes() const selectReadAlerts = createAppSelector( [ // Type of `state` is set to `RootState`, no need to manually set the type // highlight-start - state => state.alerts + state => state.alerts, // highlight-end ], - alerts => alerts.filter(({ read }) => read) + alerts => alerts.filter(({ read }) => read), ) diff --git a/docs/examples/createSelector/withTypes.ts b/docs/examples/createSelector/withTypes.ts index dd00e745d..33dd58111 100644 --- a/docs/examples/createSelector/withTypes.ts +++ b/docs/examples/createSelector/withTypes.ts @@ -11,8 +11,8 @@ const selectTodoIds = createAppSelector( [ // Type of `state` is set to `RootState`, no need to manually set the type // highlight-start - state => state.todos + state => state.todos, // highlight-end ], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) diff --git a/docs/examples/createStructuredSelector/MyComponent.tsx b/docs/examples/createStructuredSelector/MyComponent.tsx index 6b0061959..9527a6d55 100644 --- a/docs/examples/createStructuredSelector/MyComponent.tsx +++ b/docs/examples/createStructuredSelector/MyComponent.tsx @@ -9,7 +9,7 @@ interface Props { const MyComponent: FC = ({ id }) => { const { todos, alerts, todoById } = useSelector((state: RootState) => - structuredSelector(state, id) + structuredSelector(state, id), ) return ( diff --git a/docs/examples/createStructuredSelector/modernUseCase.ts b/docs/examples/createStructuredSelector/modernUseCase.ts index 1fa580ee9..9d34b7f81 100644 --- a/docs/examples/createStructuredSelector/modernUseCase.ts +++ b/docs/examples/createStructuredSelector/modernUseCase.ts @@ -15,9 +15,9 @@ export const structuredSelector = createStructuredSelector( { todos: (state: RootState) => state.todos, alerts: (state: RootState) => state.alerts, - todoById: (state: RootState, id: number) => state.todos[id] + todoById: (state: RootState, id: number) => state.todos[id], }, - createSelector + createSelector, ) // Is essentially the same as this: @@ -25,13 +25,13 @@ export const selector = createSelector( [ (state: RootState) => state.todos, (state: RootState) => state.alerts, - (state: RootState, id: number) => state.todos[id] + (state: RootState, id: number) => state.todos[id], ], (todos, alerts, todoById) => { return { todos, alerts, - todoById + todoById, } - } + }, ) diff --git a/docs/examples/createStructuredSelector/withTypes.ts b/docs/examples/createStructuredSelector/withTypes.ts index d0b083120..8e71aa067 100644 --- a/docs/examples/createStructuredSelector/withTypes.ts +++ b/docs/examples/createStructuredSelector/withTypes.ts @@ -14,5 +14,5 @@ const structuredAppSelector = createStructuredAppSelector({ todos: state => state.todos, // highlight-end alerts: state => state.alerts, - todoById: (state, id: number) => state.todos[id] + todoById: (state, id: number) => state.todos[id], }) diff --git a/docs/examples/development-only-stability-checks/identityFunctionCheck.ts b/docs/examples/development-only-stability-checks/identityFunctionCheck.ts index e38f2321e..5168c481e 100644 --- a/docs/examples/development-only-stability-checks/identityFunctionCheck.ts +++ b/docs/examples/development-only-stability-checks/identityFunctionCheck.ts @@ -12,5 +12,5 @@ const selectTodos = createSelector( // ❌ BAD: Does not contain transformation logic. todos => todos, // Will override the global setting. - { devModeChecks: { identityFunctionCheck: 'always' } } + { devModeChecks: { identityFunctionCheck: 'always' } }, ) diff --git a/docs/examples/development-only-stability-checks/inputStabilityCheck.ts b/docs/examples/development-only-stability-checks/inputStabilityCheck.ts index 2da173309..107fa0ee6 100644 --- a/docs/examples/development-only-stability-checks/inputStabilityCheck.ts +++ b/docs/examples/development-only-stability-checks/inputStabilityCheck.ts @@ -11,9 +11,9 @@ const selectCompletedTodosLength = createSelector( // ❌ Incorrect Use Case: This input selector will not be // memoized properly since it always returns a new reference. (state: RootState) => - state.todos.filter(({ completed }) => completed === true) + state.todos.filter(({ completed }) => completed === true), ], completedTodos => completedTodos.length, // Will override the global setting. - { devModeChecks: { inputStabilityCheck: 'always' } } + { devModeChecks: { inputStabilityCheck: 'always' } }, ) diff --git a/docs/examples/handling-empty-array-results/fallbackToEmptyArray.ts b/docs/examples/handling-empty-array-results/fallbackToEmptyArray.ts index f0bdcfaaf..e478c0dff 100644 --- a/docs/examples/handling-empty-array-results/fallbackToEmptyArray.ts +++ b/docs/examples/handling-empty-array-results/fallbackToEmptyArray.ts @@ -11,5 +11,5 @@ const selectCompletedTodos = createSelector( [(state: RootState) => state.todos], todos => { return fallbackToEmptyArray(todos.filter(todo => todo.completed === true)) - } + }, ) diff --git a/docs/examples/handling-empty-array-results/firstPattern.ts b/docs/examples/handling-empty-array-results/firstPattern.ts index 087f67c8c..38ade2cce 100644 --- a/docs/examples/handling-empty-array-results/firstPattern.ts +++ b/docs/examples/handling-empty-array-results/firstPattern.ts @@ -16,5 +16,5 @@ const selectCompletedTodos = createSelector( todos => { const completedTodos = todos.filter(todo => todo.completed === true) return completedTodos.length === 0 ? EMPTY_ARRAY : completedTodos - } + }, ) diff --git a/docs/examples/lruMemoize/usingWithCreateSelector.ts b/docs/examples/lruMemoize/usingWithCreateSelector.ts index e91758b81..cc49e4128 100644 --- a/docs/examples/lruMemoize/usingWithCreateSelector.ts +++ b/docs/examples/lruMemoize/usingWithCreateSelector.ts @@ -19,13 +19,13 @@ const selectTodoIds = createSelector( memoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 + maxSize: 10, }, argsMemoize: lruMemoize, argsMemoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 - } - } + maxSize: 10, + }, + }, ) diff --git a/docs/examples/lruMemoize/usingWithCreateSelectorCreator.ts b/docs/examples/lruMemoize/usingWithCreateSelectorCreator.ts index db1e51d57..71f3261c2 100644 --- a/docs/examples/lruMemoize/usingWithCreateSelectorCreator.ts +++ b/docs/examples/lruMemoize/usingWithCreateSelectorCreator.ts @@ -16,17 +16,17 @@ const createSelectorShallowEqual = createSelectorCreator({ memoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 + maxSize: 10, }, argsMemoize: lruMemoize, argsMemoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 - } + maxSize: 10, + }, }) const selectTodoIds = createSelectorShallowEqual( [(state: RootState) => state.todos], - todos => todos.map(todo => todo.id) + todos => todos.map(todo => todo.id), ) diff --git a/docs/examples/unstable_autotrackMemoize/usingWithCreateSelector.ts b/docs/examples/unstable_autotrackMemoize/usingWithCreateSelector.ts index 4b4e5b87d..21be0281f 100644 --- a/docs/examples/unstable_autotrackMemoize/usingWithCreateSelector.ts +++ b/docs/examples/unstable_autotrackMemoize/usingWithCreateSelector.ts @@ -8,5 +8,5 @@ export interface RootState { const selectTodoIds = createSelector( [(state: RootState) => state.todos], todos => todos.map(todo => todo.id), - { memoize: unstable_autotrackMemoize } + { memoize: unstable_autotrackMemoize }, ) diff --git a/docs/examples/unstable_autotrackMemoize/usingWithCreateSelectorCreator.ts b/docs/examples/unstable_autotrackMemoize/usingWithCreateSelectorCreator.ts index aa021f668..9796b8dc9 100644 --- a/docs/examples/unstable_autotrackMemoize/usingWithCreateSelectorCreator.ts +++ b/docs/examples/unstable_autotrackMemoize/usingWithCreateSelectorCreator.ts @@ -2,10 +2,10 @@ import { createSelectorCreator, unstable_autotrackMemoize } from 'reselect' import type { RootState } from './usingWithCreateSelector' const createSelectorAutotrack = createSelectorCreator({ - memoize: unstable_autotrackMemoize + memoize: unstable_autotrackMemoize, }) const selectTodoIds = createSelectorAutotrack( [(state: RootState) => state.todos], - todos => todos.map(todo => todo.id) + todos => todos.map(todo => todo.id), ) diff --git a/docs/examples/weakMapMemoize/cacheSizeProblem.ts b/docs/examples/weakMapMemoize/cacheSizeProblem.ts index 21c33b275..b44980d27 100644 --- a/docs/examples/weakMapMemoize/cacheSizeProblem.ts +++ b/docs/examples/weakMapMemoize/cacheSizeProblem.ts @@ -9,16 +9,16 @@ const state: RootState = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], - (items, category) => items.filter(item => item.category === category) + (items, category) => items.filter(item => item.category === category), ) selectItemsByCategory(state, 'Electronics') // Selector runs diff --git a/docs/examples/weakMapMemoize/cacheSizeSolution.ts b/docs/examples/weakMapMemoize/cacheSizeSolution.ts index 4a07a9704..3880509e9 100644 --- a/docs/examples/weakMapMemoize/cacheSizeSolution.ts +++ b/docs/examples/weakMapMemoize/cacheSizeSolution.ts @@ -6,20 +6,20 @@ const state: RootState = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], (items, category) => items.filter(item => item.category === category), { memoize: weakMapMemoize, - argsMemoize: weakMapMemoize - } + argsMemoize: weakMapMemoize, + }, ) selectItemsByCategory(state, 'Electronics') // Selector runs diff --git a/docs/examples/weakMapMemoize/setMaxSize.ts b/docs/examples/weakMapMemoize/setMaxSize.ts index be8741a9e..a10d66d99 100644 --- a/docs/examples/weakMapMemoize/setMaxSize.ts +++ b/docs/examples/weakMapMemoize/setMaxSize.ts @@ -4,13 +4,13 @@ import type { RootState } from './cacheSizeProblem' const selectItemsByCategory = createSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], (items, category) => items.filter(item => item.category === category), { memoize: lruMemoize, memoizeOptions: { - maxSize: 10 - } - } + maxSize: 10, + }, + }, ) diff --git a/docs/examples/weakMapMemoize/usingWithCreateSelector.ts b/docs/examples/weakMapMemoize/usingWithCreateSelector.ts index a284fdc1e..3ab0b308d 100644 --- a/docs/examples/weakMapMemoize/usingWithCreateSelector.ts +++ b/docs/examples/weakMapMemoize/usingWithCreateSelector.ts @@ -6,20 +6,20 @@ const state: RootState = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], (items, category) => items.filter(item => item.category === category), { memoize: weakMapMemoize, - argsMemoize: weakMapMemoize - } + argsMemoize: weakMapMemoize, + }, ) selectItemsByCategory(state, 'Electronics') // Selector runs diff --git a/docs/examples/weakMapMemoize/usingWithCreateSelectorCreator.ts b/docs/examples/weakMapMemoize/usingWithCreateSelectorCreator.ts index 31a226bce..30839857e 100644 --- a/docs/examples/weakMapMemoize/usingWithCreateSelectorCreator.ts +++ b/docs/examples/weakMapMemoize/usingWithCreateSelectorCreator.ts @@ -6,21 +6,21 @@ const state: RootState = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const createSelectorWeakMap = createSelectorCreator({ memoize: weakMapMemoize, - argsMemoize: weakMapMemoize + argsMemoize: weakMapMemoize, }) const selectItemsByCategory = createSelectorWeakMap( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], - (items, category) => items.filter(item => item.category === category) + (items, category) => items.filter(item => item.category === category), ) selectItemsByCategory(state, 'Electronics') // Selector runs diff --git a/docs/examples/weakMapMemoize/withUseMemo.tsx b/docs/examples/weakMapMemoize/withUseMemo.tsx index 79fcefde0..28e51e12b 100644 --- a/docs/examples/weakMapMemoize/withUseMemo.tsx +++ b/docs/examples/weakMapMemoize/withUseMemo.tsx @@ -6,7 +6,7 @@ import type { RootState } from './cacheSizeProblem' const makeSelectItemsByCategory = (category: string) => createSelector([(state: RootState) => state.items], items => - items.filter(item => item.category === category) + items.filter(item => item.category === category), ) interface Props { @@ -16,7 +16,7 @@ interface Props { const MyComponent: FC = ({ category }) => { const selectItemsByCategory = useMemo( () => makeSelectItemsByCategory(category), - [category] + [category], ) const itemsByCategory = useSelector(selectItemsByCategory) diff --git a/src/autotrackMemoize/autotrackMemoize.ts b/src/autotrackMemoize/autotrackMemoize.ts index 2a08ccd71..9426d3e1a 100644 --- a/src/autotrackMemoize/autotrackMemoize.ts +++ b/src/autotrackMemoize/autotrackMemoize.ts @@ -72,7 +72,7 @@ export function autotrackMemoize(func: Func) { // we reference arguments instead of spreading them for performance reasons const node: Node> = createNode( - [] as unknown as Record + [] as unknown as Record, ) let lastArgs: IArguments | null = null diff --git a/src/autotrackMemoize/autotracking.ts b/src/autotrackMemoize/autotracking.ts index cf8c71c08..324d62a55 100644 --- a/src/autotrackMemoize/autotracking.ts +++ b/src/autotrackMemoize/autotracking.ts @@ -131,11 +131,11 @@ type CellValue> = T extends Cell ? U : never export function setValue>( storage: T, - value: CellValue + value: CellValue, ): void { if (!(storage instanceof Cell)) { throw new TypeError( - 'setValue must be passed a tracked store created with `createStorage`.' + 'setValue must be passed a tracked store created with `createStorage`.', ) } @@ -144,7 +144,7 @@ export function setValue>( export function createCell( initialValue: T, - isEqual: EqualityFn = tripleEq + isEqual: EqualityFn = tripleEq, ): Cell { return new Cell(initialValue, isEqual) } @@ -152,7 +152,7 @@ export function createCell( export function createCache(fn: () => T): TrackingCache { assertIsFunction( fn, - 'the first parameter to `createCache` must be a function' + 'the first parameter to `createCache` must be a function', ) return new TrackingCache(fn) diff --git a/src/autotrackMemoize/proxy.ts b/src/autotrackMemoize/proxy.ts index 550a5ad4c..4ff7f01bf 100644 --- a/src/autotrackMemoize/proxy.ts +++ b/src/autotrackMemoize/proxy.ts @@ -7,7 +7,7 @@ import { consumeTag, createTag, dirtyCollection, - dirtyTag + dirtyTag, } from './tracking' export const REDUX_PROXY_LABEL = /* @__PURE__ */ Symbol() @@ -81,14 +81,14 @@ const objectProxyHandler = { getOwnPropertyDescriptor( node: Node, - prop: string | symbol + prop: string | symbol, ): PropertyDescriptor | undefined { return Reflect.getOwnPropertyDescriptor(node.value, prop) }, has(node: Node, prop: string | symbol): boolean { return Reflect.has(node.value, prop) - } + }, } class ArrayTreeNode> implements Node { @@ -120,18 +120,18 @@ const arrayProxyHandler = { getOwnPropertyDescriptor( [node]: [Node], - prop: string | symbol + prop: string | symbol, ): PropertyDescriptor | undefined { return objectProxyHandler.getOwnPropertyDescriptor(node, prop) }, has([node]: [Node], prop: string | symbol): boolean { return objectProxyHandler.has(node, prop) - } + }, } export function createNode | Record>( - value: T + value: T, ): Node { if (Array.isArray(value)) { return new ArrayTreeNode(value) @@ -147,7 +147,7 @@ const keysMap = new WeakMap< export function updateNode | Record>( node: Node, - newValue: T + newValue: T, ): void { const { value, tags, children } = node diff --git a/src/autotrackMemoize/tracking.ts b/src/autotrackMemoize/tracking.ts index 9862175c1..e7d464c9f 100644 --- a/src/autotrackMemoize/tracking.ts +++ b/src/autotrackMemoize/tracking.ts @@ -2,7 +2,7 @@ import type { Cell } from './autotracking' import { getValue as consumeTag, createCell as createStorage, - setValue + setValue, } from './autotracking' export type Tag = Cell @@ -20,7 +20,7 @@ export function dirtyTag(tag: Tag, value: any): void { export interface Node< T extends Array | Record = | Array - | Record + | Record, > { collectionTag: Tag | null tag: Tag | null diff --git a/src/autotrackMemoize/utils.ts b/src/autotrackMemoize/utils.ts index cef655a08..07dcf8892 100644 --- a/src/autotrackMemoize/utils.ts +++ b/src/autotrackMemoize/utils.ts @@ -1,6 +1,6 @@ export function assert( condition: any, - msg = 'Assertion failed!' + msg = 'Assertion failed!', ): asserts condition { if (!condition) { console.error(msg) diff --git a/src/createSelectorCreator.ts b/src/createSelectorCreator.ts index cc64ab6fd..dfce9c414 100644 --- a/src/createSelectorCreator.ts +++ b/src/createSelectorCreator.ts @@ -13,7 +13,7 @@ import type { SelectorArray, SetRequired, Simplify, - UnknownMemoizer + UnknownMemoizer, } from './types' import { @@ -21,7 +21,7 @@ import { collectInputSelectorResults, ensureIsArray, getDependencies, - getDevModeChecksExecutionInfo + getDevModeChecksExecutionInfo, } from './utils' /** @@ -36,7 +36,7 @@ import { export interface CreateSelectorFunction< MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - StateType = any + StateType = any, > { /** * Creates a memoized selector function. @@ -54,7 +54,7 @@ export interface CreateSelectorFunction< , Result>( ...createSelectorArgs: [ ...inputSelectors: InputSelectors, - combiner: Combiner + combiner: Combiner, ] ): OutputSelector< InputSelectors, @@ -81,7 +81,7 @@ export interface CreateSelectorFunction< InputSelectors extends SelectorArray, Result, OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction, - OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction + OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction, >( ...createSelectorArgs: [ ...inputSelectors: InputSelectors, @@ -93,7 +93,7 @@ export interface CreateSelectorFunction< OverrideMemoizeFunction, OverrideArgsMemoizeFunction > - > + >, ] ): OutputSelector< InputSelectors, @@ -122,7 +122,7 @@ export interface CreateSelectorFunction< InputSelectors extends SelectorArray, Result, OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction, - OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction + OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction, >( inputSelectors: [...InputSelectors], combiner: Combiner, @@ -133,7 +133,7 @@ export interface CreateSelectorFunction< OverrideMemoizeFunction, OverrideArgsMemoizeFunction > - > + >, ): OutputSelector< InputSelectors, Result, @@ -219,7 +219,7 @@ export interface CreateSelectorFunction< */ export function createSelectorCreator< MemoizeFunction extends UnknownMemoizer, - ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, >( options: Simplify< SetRequired< @@ -231,7 +231,7 @@ export function createSelectorCreator< >, 'memoize' > - > + >, ): CreateSelectorFunction /** @@ -287,7 +287,7 @@ export function createSelectorCreator< | SetRequired< CreateSelectorOptions, 'memoize' - > + >, >( memoizeOrOptions: MemoizeOrOptions, ...memoizeOptionsFromArgs: MemoizeOrOptions extends SetRequired< @@ -304,7 +304,7 @@ export function createSelectorCreator< > = typeof memoizeOrOptions === 'function' ? { memoize: memoizeOrOptions as MemoizeFunction, - memoizeOptions: memoizeOptionsFromArgs + memoizeOptions: memoizeOptionsFromArgs, } : memoizeOrOptions @@ -312,7 +312,7 @@ export function createSelectorCreator< InputSelectors extends SelectorArray, Result, OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction, - OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction + OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction, >( ...createSelectorArgs: [ ...inputSelectors: [...InputSelectors], @@ -322,7 +322,7 @@ export function createSelectorCreator< ArgsMemoizeFunction, OverrideMemoizeFunction, OverrideArgsMemoizeFunction - > + >, ] ) => { let recomputations = 0 @@ -358,21 +358,21 @@ export function createSelectorCreator< assertIsFunction( resultFunc, - `createSelector expects an output function after the inputs, but received: [${typeof resultFunc}]` + `createSelector expects an output function after the inputs, but received: [${typeof resultFunc}]`, ) // Determine which set of options we're using. Prefer options passed directly, // but fall back to options given to `createSelectorCreator`. const combinedOptions = { ...createSelectorCreatorOptions, - ...directlyPassedOptions + ...directlyPassedOptions, } const { memoize, memoizeOptions = [], argsMemoize = weakMapMemoize, - argsMemoizeOptions = [] + argsMemoizeOptions = [], } = combinedOptions // Simplifying assumption: it's unlikely that the first options arg of the provided memoizer @@ -384,63 +384,69 @@ export function createSelectorCreator< const finalArgsMemoizeOptions = ensureIsArray(argsMemoizeOptions) const dependencies = getDependencies(createSelectorArgs) as InputSelectors - const memoizedResultFunc = memoize(function recomputationWrapper() { - recomputations++ - // apply arguments instead of spreading for performance. - // @ts-ignore - return (resultFunc as Combiner).apply( - null, - arguments as unknown as Parameters> - ) - }, ...finalMemoizeOptions) as Combiner & + const memoizedResultFunc = memoize( + function recomputationWrapper() { + recomputations++ + // apply arguments instead of spreading for performance. + // @ts-ignore + return (resultFunc as Combiner).apply( + null, + arguments as unknown as Parameters>, + ) + }, + ...finalMemoizeOptions, + ) as Combiner & ExtractMemoizerFields let firstRun = true // If a selector is called with the exact same arguments we don't need to traverse our dependencies again. - const selector = argsMemoize(function dependenciesChecker() { - dependencyRecomputations++ - /** Return values of input selectors which the `resultFunc` takes as arguments. */ - const inputSelectorResults = collectInputSelectorResults( - dependencies, - arguments - ) + const selector = argsMemoize( + function dependenciesChecker() { + dependencyRecomputations++ + /** Return values of input selectors which the `resultFunc` takes as arguments. */ + const inputSelectorResults = collectInputSelectorResults( + dependencies, + arguments, + ) - // apply arguments instead of spreading for performance. - // @ts-ignore - lastResult = memoizedResultFunc.apply(null, inputSelectorResults) + // apply arguments instead of spreading for performance. + // @ts-ignore + lastResult = memoizedResultFunc.apply(null, inputSelectorResults) - if (process.env.NODE_ENV !== 'production') { - const { devModeChecks = {} } = combinedOptions - const { identityFunctionCheck, inputStabilityCheck } = - getDevModeChecksExecutionInfo(firstRun, devModeChecks) - if (identityFunctionCheck.shouldRun) { - identityFunctionCheck.run( - resultFunc as Combiner, - inputSelectorResults, - lastResult - ) - } + if (process.env.NODE_ENV !== 'production') { + const { devModeChecks = {} } = combinedOptions + const { identityFunctionCheck, inputStabilityCheck } = + getDevModeChecksExecutionInfo(firstRun, devModeChecks) + if (identityFunctionCheck.shouldRun) { + identityFunctionCheck.run( + resultFunc as Combiner, + inputSelectorResults, + lastResult, + ) + } - if (inputStabilityCheck.shouldRun) { - // make a second copy of the params, to check if we got the same results - const inputSelectorResultsCopy = collectInputSelectorResults( - dependencies, - arguments - ) + if (inputStabilityCheck.shouldRun) { + // make a second copy of the params, to check if we got the same results + const inputSelectorResultsCopy = collectInputSelectorResults( + dependencies, + arguments, + ) - inputStabilityCheck.run( - { inputSelectorResults, inputSelectorResultsCopy }, - { memoize, memoizeOptions: finalMemoizeOptions }, - arguments - ) - } + inputStabilityCheck.run( + { inputSelectorResults, inputSelectorResultsCopy }, + { memoize, memoizeOptions: finalMemoizeOptions }, + arguments, + ) + } - if (firstRun) firstRun = false - } + if (firstRun) firstRun = false + } - return lastResult - }, ...finalArgsMemoizeOptions) as unknown as Selector< + return lastResult + }, + ...finalArgsMemoizeOptions, + ) as unknown as Selector< GetStateFromSelectors, Result, GetParamsFromSelectors @@ -461,7 +467,7 @@ export function createSelectorCreator< recomputations = 0 }, memoize, - argsMemoize + argsMemoize, }) as OutputSelector< InputSelectors, Result, @@ -471,7 +477,7 @@ export function createSelectorCreator< } Object.assign(createSelector, { - withTypes: () => createSelector + withTypes: () => createSelector, }) return createSelector as CreateSelectorFunction< diff --git a/src/createStructuredSelector.ts b/src/createStructuredSelector.ts index 3dc17d6e4..787d7657d 100644 --- a/src/createStructuredSelector.ts +++ b/src/createStructuredSelector.ts @@ -7,7 +7,7 @@ import type { OutputSelector, Selector, Simplify, - UnknownMemoizer + UnknownMemoizer, } from './types' import { assertIsObject } from './utils' import type { weakMapMemoize } from './weakMapMemoize' @@ -154,15 +154,16 @@ export type TypedStructuredSelectorCreator = * @see {@link https://reselect.js.org/api/createStructuredSelector `createStructuredSelector`} */ < - InputSelectorsObject extends RootStateSelectors = RootStateSelectors, + InputSelectorsObject extends + RootStateSelectors = RootStateSelectors, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, >( inputSelectorsObject: InputSelectorsObject, selectorCreator?: CreateSelectorFunction< MemoizeFunction, ArgsMemoizeFunction - > + >, ) => OutputSelector< ObjectValuesToTuple, Simplify>, @@ -303,13 +304,13 @@ export interface StructuredSelectorCreator { < InputSelectorsObject extends SelectorsObject, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, >( inputSelectorsObject: InputSelectorsObject, selectorCreator?: CreateSelectorFunction< MemoizeFunction, ArgsMemoizeFunction - > + >, ): OutputSelector< ObjectValuesToTuple, Simplify>, @@ -356,7 +357,7 @@ export interface StructuredSelectorCreator { * @since 5.1.0 */ withTypes: < - OverrideStateType extends StateType + OverrideStateType extends StateType, >() => StructuredSelectorCreator } @@ -419,7 +420,7 @@ export const createStructuredSelector: StructuredSelectorCreator = < InputSelectorsObject extends SelectorsObject, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, >( inputSelectorsObject: InputSelectorsObject, selectorCreator: CreateSelectorFunction< @@ -428,16 +429,16 @@ export const createStructuredSelector: StructuredSelectorCreator = > = createSelector as CreateSelectorFunction< MemoizeFunction, ArgsMemoizeFunction - > + >, ) => { assertIsObject( inputSelectorsObject, 'createStructuredSelector expects first argument to be an object ' + - `where each property is a selector, instead received a ${typeof inputSelectorsObject}` + `where each property is a selector, instead received a ${typeof inputSelectorsObject}`, ) const inputSelectorKeys = Object.keys(inputSelectorsObject) const dependencies = inputSelectorKeys.map( - key => inputSelectorsObject[key] + key => inputSelectorsObject[key], ) const structuredSelector = selectorCreator( dependencies, @@ -446,9 +447,9 @@ export const createStructuredSelector: StructuredSelectorCreator = composition[inputSelectorKeys[index]] = value return composition }, {}) - } + }, ) return structuredSelector }, - { withTypes: () => createStructuredSelector } + { withTypes: () => createStructuredSelector }, ) as StructuredSelectorCreator diff --git a/src/devModeChecks/identityFunctionCheck.ts b/src/devModeChecks/identityFunctionCheck.ts index 487a8ece5..26ee1d93d 100644 --- a/src/devModeChecks/identityFunctionCheck.ts +++ b/src/devModeChecks/identityFunctionCheck.ts @@ -20,7 +20,7 @@ import type { AnyFunction } from '../types' export const runIdentityFunctionCheck = ( resultFunc: AnyFunction, inputSelectorsResults: unknown[], - outputSelectorResult: unknown + outputSelectorResult: unknown, ) => { if ( inputSelectorsResults.length === 1 && @@ -46,7 +46,7 @@ export const runIdentityFunctionCheck = ( '\n`createSelector([state => state.todos], todos => todos)`' + '\nThis could lead to inefficient memoization and unnecessary re-renders.' + '\nEnsure transformation logic is in the result function, and extraction logic is in the input selectors.', - { stack } + { stack }, ) } } diff --git a/src/devModeChecks/inputStabilityCheck.ts b/src/devModeChecks/inputStabilityCheck.ts index 307056935..61e9e49e8 100644 --- a/src/devModeChecks/inputStabilityCheck.ts +++ b/src/devModeChecks/inputStabilityCheck.ts @@ -25,7 +25,7 @@ export const runInputStabilityCheck = ( 'memoize' | 'memoizeOptions' > >, - inputSelectorArgs: unknown[] | IArguments + inputSelectorArgs: unknown[] | IArguments, ) => { const { memoize, memoizeOptions } = options const { inputSelectorResults, inputSelectorResultsCopy } = @@ -52,8 +52,8 @@ export const runInputStabilityCheck = ( arguments: inputSelectorArgs, firstInputs: inputSelectorResults, secondInputs: inputSelectorResultsCopy, - stack - } + stack, + }, ) } } diff --git a/src/devModeChecks/setGlobalDevModeChecks.ts b/src/devModeChecks/setGlobalDevModeChecks.ts index 6b77babe4..712d5064d 100644 --- a/src/devModeChecks/setGlobalDevModeChecks.ts +++ b/src/devModeChecks/setGlobalDevModeChecks.ts @@ -9,7 +9,7 @@ import type { DevModeChecks } from '../types' */ export const globalDevModeChecks: DevModeChecks = { inputStabilityCheck: 'once', - identityFunctionCheck: 'once' + identityFunctionCheck: 'once', } /** @@ -57,7 +57,7 @@ export const globalDevModeChecks: DevModeChecks = { * @public */ export const setGlobalDevModeChecks = ( - devModeChecks: Partial + devModeChecks: Partial, ) => { Object.assign(globalDevModeChecks, devModeChecks) } diff --git a/src/index.ts b/src/index.ts index c7a9d66a3..a286ae76f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -7,7 +7,7 @@ export type { SelectorResultsMap, SelectorsObject, StructuredSelectorCreator, - TypedStructuredSelectorCreator + TypedStructuredSelectorCreator, } from './createStructuredSelector' export { setGlobalDevModeChecks } from './devModeChecks/setGlobalDevModeChecks' export { lruMemoize, referenceEqualityCheck } from './lruMemoize' @@ -30,7 +30,7 @@ export type { Selector, SelectorArray, SelectorResultArray, - UnknownMemoizer + UnknownMemoizer, } from './types' export { weakMapMemoize } from './weakMapMemoize' export type { WeakMapMemoizeOptions } from './weakMapMemoize' diff --git a/src/lruMemoize.ts b/src/lruMemoize.ts index b2d0e7775..975c38688 100644 --- a/src/lruMemoize.ts +++ b/src/lruMemoize.ts @@ -2,7 +2,7 @@ import type { AnyFunction, DefaultMemoizeFields, EqualityFn, - Simplify + Simplify, } from './types' import type { NOT_FOUND_TYPE } from './utils' @@ -44,7 +44,7 @@ function createSingletonCache(equals: EqualityFn): Cache { clear() { entry = undefined - } + }, } } @@ -105,7 +105,7 @@ export const referenceEqualityCheck: EqualityFn = (a, b) => a === b export function createCacheKeyComparator(equalityCheck: EqualityFn) { return function areArgumentsShallowlyEqual( prev: unknown[] | IArguments | null, - next: unknown[] | IArguments | null + next: unknown[] | IArguments | null, ): boolean { if (prev === null || next === null || prev.length !== next.length) { return false @@ -187,7 +187,7 @@ export interface LruMemoizeOptions { */ export function lruMemoize( func: Func, - equalityCheckOrOptions?: EqualityFn | LruMemoizeOptions> + equalityCheckOrOptions?: EqualityFn | LruMemoizeOptions>, ) { const providedOptions = typeof equalityCheckOrOptions === 'object' @@ -197,7 +197,7 @@ export function lruMemoize( const { equalityCheck = referenceEqualityCheck, maxSize = 1, - resultEqualityCheck + resultEqualityCheck, } = providedOptions const comparator = createCacheKeyComparator(equalityCheck) @@ -220,7 +220,7 @@ export function lruMemoize( if (resultEqualityCheck) { const entries = cache.getEntries() const matchingEntry = entries.find(entry => - resultEqualityCheck(entry.value as ReturnType, value) + resultEqualityCheck(entry.value as ReturnType, value), ) if (matchingEntry) { diff --git a/src/types.ts b/src/types.ts index 742257a8b..26f19a774 100644 --- a/src/types.ts +++ b/src/types.ts @@ -24,7 +24,7 @@ export type { MergeParameters } from './versionedTypes' export type Selector< State = any, Result = unknown, - Params extends readonly any[] = any[] + Params extends readonly any[] = any[], > = Distribute< /** * A function that takes a state and returns data that is based on that state. @@ -65,7 +65,7 @@ export interface CreateSelectorOptions< MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, OverrideMemoizeFunction extends UnknownMemoizer = never, - OverrideArgsMemoizeFunction extends UnknownMemoizer = never + OverrideArgsMemoizeFunction extends UnknownMemoizer = never, > { /** * Reselect performs additional checks in development mode to help identify @@ -179,7 +179,7 @@ export type OutputSelectorFields< InputSelectors extends SelectorArray = SelectorArray, Result = unknown, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, > = { /** * The final function passed to `createSelector`. Otherwise known as the `combiner`. @@ -253,7 +253,7 @@ export type OutputSelector< InputSelectors extends SelectorArray = SelectorArray, Result = unknown, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, > = Selector< GetStateFromSelectors, Result, @@ -385,7 +385,7 @@ export type GetParamsFromSelectors = ArrayTail< * @public */ export type UnknownMemoizer< - FunctionType extends UnknownFunction = UnknownFunction + FunctionType extends UnknownFunction = UnknownFunction, > = (func: FunctionType, ...options: any[]) => FunctionType /** @@ -398,7 +398,7 @@ export type UnknownMemoizer< * @public */ export type MemoizeOptionsFromParameters< - MemoizeFunction extends UnknownMemoizer + MemoizeFunction extends UnknownMemoizer, > = | ( | NonFunctionType[0]> @@ -421,7 +421,7 @@ export type MemoizeOptionsFromParameters< */ export type OverrideMemoizeOptions< MemoizeFunction extends UnknownMemoizer, - OverrideMemoizeFunction extends UnknownMemoizer = never + OverrideMemoizeFunction extends UnknownMemoizer = never, > = IfNever< OverrideMemoizeFunction, Simplify>, @@ -557,7 +557,7 @@ export type Distribute = T extends T ? T : never */ export type FirstArrayElement = ArrayType extends readonly [ unknown, - ...unknown[] + ...unknown[], ] ? ArrayType[0] : never @@ -569,7 +569,7 @@ export type FirstArrayElement = ArrayType extends readonly [ */ export type ArrayTail = ArrayType extends readonly [ unknown, - ...infer Tail + ...infer Tail, ] ? Tail : [] @@ -670,11 +670,10 @@ type Push = [...T, V] * * @internal */ -type LastOf = UnionToIntersection< - T extends any ? () => T : never -> extends () => infer R - ? R - : never +type LastOf = + UnionToIntersection T : never> extends () => infer R + ? R + : never /** * TS4.1+ @@ -685,7 +684,7 @@ type LastOf = UnionToIntersection< export type TuplifyUnion< T, L = LastOf, - N = [T] extends [never] ? true : false + N = [T] extends [never] ? true : false, > = true extends N ? [] : Push>, L> /** @@ -697,7 +696,7 @@ export type TuplifyUnion< export type ObjectValuesToTuple< T, KS extends any[] = TuplifyUnion, - R extends any[] = [] + R extends any[] = [], > = KS extends [infer K, ...infer KT] ? ObjectValuesToTuple : R @@ -790,8 +789,8 @@ export type BuiltIn = export type Expand = T extends (...args: infer A) => infer R ? (...args: Expand) => Expand : T extends infer O - ? { [K in keyof O]: O[K] } - : never + ? { [K in keyof O]: O[K] } + : never /** * Expand an item recursively. @@ -802,10 +801,10 @@ export type Expand = T extends (...args: infer A) => infer R export type ExpandRecursively = T extends (...args: infer A) => infer R ? (...args: ExpandRecursively) => ExpandRecursively : T extends object - ? T extends infer O - ? { [K in keyof O]: ExpandRecursively } - : never - : T + ? T extends infer O + ? { [K in keyof O]: ExpandRecursively } + : never + : T /** * @internal @@ -867,10 +866,10 @@ export type ComputeDeep = A extends BuiltIn } & unknown)[] : A : A extends readonly any[] - ? A extends readonly Record[] - ? readonly ({ - [K in keyof A[number]]: ComputeDeep - } & unknown)[] - : A - : { [K in keyof A]: ComputeDeep } & unknown + ? A extends readonly Record[] + ? readonly ({ + [K in keyof A[number]]: ComputeDeep + } & unknown)[] + : A + : { [K in keyof A]: ComputeDeep } & unknown > diff --git a/src/utils.ts b/src/utils.ts index 672fcd320..e0fbe842a 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -6,7 +6,7 @@ import type { DevModeChecks, Selector, SelectorArray, - DevModeChecksExecutionInfo + DevModeChecksExecutionInfo, } from './types' export const NOT_FOUND = /* @__PURE__ */ Symbol('NOT_FOUND') @@ -22,7 +22,7 @@ export type NOT_FOUND_TYPE = typeof NOT_FOUND */ export function assertIsFunction( func: unknown, - errorMessage = `expected a function, instead received ${typeof func}` + errorMessage = `expected a function, instead received ${typeof func}`, ): asserts func is FunctionType { if (typeof func !== 'function') { throw new TypeError(errorMessage) @@ -39,7 +39,7 @@ export function assertIsFunction( */ export function assertIsObject>( object: unknown, - errorMessage = `expected an object, instead received ${typeof object}` + errorMessage = `expected an object, instead received ${typeof object}`, ): asserts object is ObjectType { if (typeof object !== 'object') { throw new TypeError(errorMessage) @@ -56,7 +56,7 @@ export function assertIsObject>( */ export function assertIsArrayOfFunctions( array: unknown[], - errorMessage = `expected all items to be functions, instead received the following types: ` + errorMessage = `expected all items to be functions, instead received the following types: `, ): asserts array is FunctionType[] { if ( !array.every((item): item is FunctionType => typeof item === 'function') @@ -65,7 +65,7 @@ export function assertIsArrayOfFunctions( .map(item => typeof item === 'function' ? `function ${item.name || 'unnamed'}()` - : typeof item + : typeof item, ) .join(', ') throw new TypeError(`${errorMessage}[${itemTypes}]`) @@ -97,7 +97,7 @@ export function getDependencies(createSelectorArgs: unknown[]) { assertIsArrayOfFunctions( dependencies, - `createSelector expects all input-selectors to be functions, but received the following types: ` + `createSelector expects all input-selectors to be functions, but received the following types: `, ) return dependencies as SelectorArray @@ -112,7 +112,7 @@ export function getDependencies(createSelectorArgs: unknown[]) { */ export function collectInputSelectorResults( dependencies: SelectorArray, - inputSelectorArgs: unknown[] | IArguments + inputSelectorArgs: unknown[] | IArguments, ) { const inputSelectorResults = [] const { length } = dependencies @@ -133,24 +133,24 @@ export function collectInputSelectorResults( */ export const getDevModeChecksExecutionInfo = ( firstRun: boolean, - devModeChecks: Partial + devModeChecks: Partial, ) => { const { identityFunctionCheck, inputStabilityCheck } = { ...globalDevModeChecks, - ...devModeChecks + ...devModeChecks, } return { identityFunctionCheck: { shouldRun: identityFunctionCheck === 'always' || (identityFunctionCheck === 'once' && firstRun), - run: runIdentityFunctionCheck + run: runIdentityFunctionCheck, }, inputStabilityCheck: { shouldRun: inputStabilityCheck === 'always' || (inputStabilityCheck === 'once' && firstRun), - run: runInputStabilityCheck - } + run: runInputStabilityCheck, + }, } satisfies DevModeChecksExecutionInfo } diff --git a/src/versionedTypes/ts47-mergeParameters.ts b/src/versionedTypes/ts47-mergeParameters.ts index fdac93660..055e934e0 100644 --- a/src/versionedTypes/ts47-mergeParameters.ts +++ b/src/versionedTypes/ts47-mergeParameters.ts @@ -14,11 +14,11 @@ type LongestTuple = ArrayOfTuples extends [infer FirstArray extends unknown[]] ? FirstArray : ArrayOfTuples extends [ - infer FirstArray, - ...infer RestArrays extends unknown[][] - ] - ? LongerOfTwo> - : never + infer FirstArray, + ...infer RestArrays extends unknown[][], + ] + ? LongerOfTwo> + : never /** * Determines the longer of two array types. @@ -42,7 +42,7 @@ type LongerOfTwo = keyof ArrayTwo extends keyof ArrayOne */ type ElementAt< ArrayType extends unknown[], - Index extends PropertyKey + Index extends PropertyKey, > = Index extends keyof ArrayType ? ArrayType[Index] : unknown /** @@ -55,7 +55,7 @@ type ElementAt< */ type ElementsAtGivenIndex< ArrayOfTuples extends readonly unknown[][], - Index extends PropertyKey + Index extends PropertyKey, > = { [ArrayIndex in keyof ArrayOfTuples]: ElementAt< ArrayOfTuples[ArrayIndex], @@ -73,8 +73,8 @@ type ElementsAtGivenIndex< type Intersect = Tuple extends [] ? unknown : Tuple extends [infer Head, ...infer Tail] - ? Head & Intersect - : Tuple[number] + ? Head & Intersect + : Tuple[number] /** * Merges a tuple of arrays into a single tuple, intersecting types at each index. @@ -86,7 +86,7 @@ type Intersect = Tuple extends [] */ type MergeTuples< ArrayOfTuples extends readonly unknown[][], - LongestArray extends unknown[] = LongestTuple + LongestArray extends unknown[] = LongestTuple, > = { [Index in keyof LongestArray]: Intersect< ElementsAtGivenIndex diff --git a/src/weakMapMemoize.ts b/src/weakMapMemoize.ts index 9e31fdd41..452628349 100644 --- a/src/weakMapMemoize.ts +++ b/src/weakMapMemoize.ts @@ -5,7 +5,7 @@ import type { AnyFunction, DefaultMemoizeFields, EqualityFn, - Simplify + Simplify, } from './types' class StrongRef { @@ -76,7 +76,7 @@ function createCacheNode(): CacheNode { s: UNTERMINATED, v: undefined, o: null, - p: null + p: null, } } @@ -193,7 +193,7 @@ function maybeDeref(r: any) { */ export function weakMapMemoize( func: Func, - options: WeakMapMemoizeOptions> = {} + options: WeakMapMemoizeOptions> = {}, ) { let fnNode = createCacheNode() const { resultEqualityCheck } = options diff --git a/test/autotrackMemoize.spec.ts b/test/autotrackMemoize.spec.ts index 3be2f21a2..5e8f5b8ca 100644 --- a/test/autotrackMemoize.spec.ts +++ b/test/autotrackMemoize.spec.ts @@ -1,6 +1,6 @@ import { unstable_autotrackMemoize as autotrackMemoize, - createSelectorCreator + createSelectorCreator, } from 'reselect' import { setEnvToProd } from './testUtils' @@ -35,7 +35,7 @@ describe('Basic selector behavior with autotrack', () => { const selector = createSelector( (state: StateA) => state.a, a => a, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const firstState = { a: 1 } const firstStateNewPointer = { a: 1 } @@ -54,7 +54,7 @@ describe('Basic selector behavior with autotrack', () => { const selector = createSelector( (...params: any[]) => params.length, a => a, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) expect(selector({})).toBe(1) }) @@ -63,7 +63,7 @@ describe('Basic selector behavior with autotrack', () => { const selector = createSelector( (state: StateAB) => state.a, (state: StateAB) => state.b, - (a, b) => a + b + (a, b) => a + b, ) const state1 = { a: 1, b: 2 } expect(selector(state1)).toBe(3) @@ -84,17 +84,17 @@ describe('Basic selector behavior with autotrack', () => { return state.b }, 'not a function', - (a: any, b: any) => a + b - ) + (a: any, b: any) => a + b, + ), ).toThrow( - 'createSelector expects all input-selectors to be functions, but received the following types: [function unnamed(), function input2(), string]' + 'createSelector expects all input-selectors to be functions, but received the following types: [function unnamed(), function input2(), string]', ) expect(() => // @ts-ignore - createSelector((state: StateAB) => state.a, 'not a function') + createSelector((state: StateAB) => state.a, 'not a function'), ).toThrow( - 'createSelector expects an output function after the inputs, but received: [string]' + 'createSelector expects an output function after the inputs, but received: [string]', ) }) @@ -109,7 +109,7 @@ describe('Basic selector behavior with autotrack', () => { (state: StateAB) => state.a, (state: StateAB) => state.b, (a, b) => a + b, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const state1 = { a: 1, b: 2 } @@ -130,7 +130,7 @@ describe('Basic selector behavior with autotrack', () => { (state: StateAB) => state.a, (state: StateAB) => state.b, (a, b) => a + b, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const start = performance.now() @@ -150,7 +150,7 @@ describe('Basic selector behavior with autotrack', () => { test('memoized composite arguments', () => { const selector = createSelector( (state: StateSub) => state.sub, - sub => sub.a + sub => sub.a, ) const state1 = { sub: { a: 1 } } expect(selector(state1)).toEqual(1) @@ -166,7 +166,7 @@ describe('Basic selector behavior with autotrack', () => { [state => state.a, state => state.b], (a, b) => { return a + b - } + }, ) expect(selector({ a: 1, b: 2 })).toBe(3) expect(selector({ a: 1, b: 2 })).toBe(3) @@ -184,7 +184,7 @@ describe('Basic selector behavior with autotrack', () => { (a, b, c) => { called++ return a + b + c - } + }, ) expect(selector({ a: 1, b: 2 }, { c: 100 })).toBe(103) }) @@ -197,7 +197,7 @@ describe('Basic selector behavior with autotrack', () => { called++ throw Error('test error') }, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) expect(() => selector({ a: 1 })).toThrow('test error') expect(() => selector({ a: 1 })).toThrow('test error') @@ -213,7 +213,7 @@ describe('Basic selector behavior with autotrack', () => { if (a > 1) throw Error('test error') return a }, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const state1 = { a: 1 } const state2 = { a: 2 } diff --git a/test/benchmarks/orderOfExecution.bench.ts b/test/benchmarks/orderOfExecution.bench.ts index 34014619c..0abce80b0 100644 --- a/test/benchmarks/orderOfExecution.bench.ts +++ b/test/benchmarks/orderOfExecution.bench.ts @@ -1,213 +1,213 @@ -import type { OutputSelector, Selector } from 'reselect' -import { createSelector, lruMemoize } from 'reselect' -import type { Options } from 'tinybench' -import { bench } from 'vitest' -import type { RootState } from '../testUtils' -import { - countRecomputations, - expensiveComputation, - logFunctionInfo, - logSelectorRecomputations, - resetSelector, - runMultipleTimes, - setFunctionNames, - setupStore, - toggleCompleted, - toggleRead -} from '../testUtils' - -describe('Less vs more computation in input selectors', () => { - const store = setupStore() - const runSelector = (selector: Selector) => { - runMultipleTimes(selector, 100, store.getState()) - } - const selectorLessInInput = createSelector( - [(state: RootState) => state.todos], - todos => { - expensiveComputation() - return todos.filter(todo => todo.completed) - } - ) - const selectorMoreInInput = createSelector( - [ - (state: RootState) => { - expensiveComputation() - return state.todos - } - ], - todos => todos.filter(todo => todo.completed) - ) - - const nonMemoized = countRecomputations((state: RootState) => { - expensiveComputation() - return state.todos.filter(todo => todo.completed) - }) - const commonOptions: Options = { - iterations: 10, - time: 0 - } - setFunctionNames({ selectorLessInInput, selectorMoreInInput, nonMemoized }) - const createOptions = ( - selector: S, - commonOptions: Options = {} - ) => { - const options: Options = { - setup: (task, mode) => { - if (mode === 'warmup') return - task.opts = { - beforeEach: () => { - store.dispatch(toggleRead(1)) - }, - afterAll: () => { - logSelectorRecomputations(selector) - } - } - } - } - return { ...commonOptions, ...options } - } - bench( - selectorLessInInput, - () => { - runSelector(selectorLessInInput) - }, - createOptions(selectorLessInInput, commonOptions) - ) - bench( - selectorMoreInInput, - () => { - runSelector(selectorMoreInInput) - }, - createOptions(selectorMoreInInput, commonOptions) - ) - bench( - nonMemoized, - () => { - runSelector(nonMemoized) - }, - { - ...commonOptions, - setup: (task, mode) => { - if (mode === 'warmup') return - nonMemoized.resetRecomputations() - task.opts = { - beforeEach: () => { - store.dispatch(toggleCompleted(1)) - }, - afterAll: () => { - logFunctionInfo(nonMemoized, nonMemoized.recomputations()) - } - } - } - } - ) -}) - -// This benchmark is made to test to see at what point it becomes beneficial -// to use reselect to memoize a function that is a plain field accessor. -describe('Reselect vs standalone memoization for field access', () => { - const store = setupStore() - const runSelector = (selector: Selector) => { - runMultipleTimes(selector, 1_000_000, store.getState()) - } - const commonOptions: Options = { - // warmupIterations: 0, - // warmupTime: 0, - // iterations: 10, - // time: 0 - } - const fieldAccessorWithReselect = createSelector( - [(state: RootState) => state.users], - users => users.appSettings - ) - const fieldAccessorWithMemoize = countRecomputations( - lruMemoize((state: RootState) => { - return state.users.appSettings - }) - ) - const nonMemoizedAccessor = countRecomputations( - (state: RootState) => state.users.appSettings - ) - - setFunctionNames({ - fieldAccessorWithReselect, - fieldAccessorWithMemoize, - nonMemoizedAccessor - }) - const createOptions = ( - selector: S, - commonOptions: Options = {} - ) => { - const options: Options = { - setup: (task, mode) => { - if (mode === 'warmup') return - resetSelector(selector) - task.opts = { - beforeEach: () => { - store.dispatch(toggleCompleted(1)) - }, - afterAll: () => { - logSelectorRecomputations(selector) - } - } - } - } - return { ...commonOptions, ...options } - } - bench( - fieldAccessorWithReselect, - () => { - runSelector(fieldAccessorWithReselect) - }, - createOptions(fieldAccessorWithReselect, commonOptions) - ) - bench( - fieldAccessorWithMemoize, - () => { - runSelector(fieldAccessorWithMemoize) - }, - { - ...commonOptions, - setup: (task, mode) => { - if (mode === 'warmup') return - fieldAccessorWithMemoize.resetRecomputations() - fieldAccessorWithMemoize.clearCache() - task.opts = { - beforeEach: () => { - store.dispatch(toggleCompleted(1)) - }, - afterAll: () => { - logFunctionInfo( - fieldAccessorWithMemoize, - fieldAccessorWithMemoize.recomputations() - ) - } - } - } - } - ) - bench( - nonMemoizedAccessor, - () => { - runSelector(nonMemoizedAccessor) - }, - { - ...commonOptions, - setup: (task, mode) => { - if (mode === 'warmup') return - nonMemoizedAccessor.resetRecomputations() - task.opts = { - beforeEach: () => { - store.dispatch(toggleCompleted(1)) - }, - afterAll: () => { - logFunctionInfo( - nonMemoizedAccessor, - nonMemoizedAccessor.recomputations() - ) - } - } - } - } - ) -}) +import type { OutputSelector, Selector } from 'reselect' +import { createSelector, lruMemoize } from 'reselect' +import type { Options } from 'tinybench' +import { bench } from 'vitest' +import type { RootState } from '../testUtils' +import { + countRecomputations, + expensiveComputation, + logFunctionInfo, + logSelectorRecomputations, + resetSelector, + runMultipleTimes, + setFunctionNames, + setupStore, + toggleCompleted, + toggleRead, +} from '../testUtils' + +describe('Less vs more computation in input selectors', () => { + const store = setupStore() + const runSelector = (selector: Selector) => { + runMultipleTimes(selector, 100, store.getState()) + } + const selectorLessInInput = createSelector( + [(state: RootState) => state.todos], + todos => { + expensiveComputation() + return todos.filter(todo => todo.completed) + }, + ) + const selectorMoreInInput = createSelector( + [ + (state: RootState) => { + expensiveComputation() + return state.todos + }, + ], + todos => todos.filter(todo => todo.completed), + ) + + const nonMemoized = countRecomputations((state: RootState) => { + expensiveComputation() + return state.todos.filter(todo => todo.completed) + }) + const commonOptions: Options = { + iterations: 10, + time: 0, + } + setFunctionNames({ selectorLessInInput, selectorMoreInInput, nonMemoized }) + const createOptions = ( + selector: S, + commonOptions: Options = {}, + ) => { + const options: Options = { + setup: (task, mode) => { + if (mode === 'warmup') return + task.opts = { + beforeEach: () => { + store.dispatch(toggleRead(1)) + }, + afterAll: () => { + logSelectorRecomputations(selector) + }, + } + }, + } + return { ...commonOptions, ...options } + } + bench( + selectorLessInInput, + () => { + runSelector(selectorLessInInput) + }, + createOptions(selectorLessInInput, commonOptions), + ) + bench( + selectorMoreInInput, + () => { + runSelector(selectorMoreInInput) + }, + createOptions(selectorMoreInInput, commonOptions), + ) + bench( + nonMemoized, + () => { + runSelector(nonMemoized) + }, + { + ...commonOptions, + setup: (task, mode) => { + if (mode === 'warmup') return + nonMemoized.resetRecomputations() + task.opts = { + beforeEach: () => { + store.dispatch(toggleCompleted(1)) + }, + afterAll: () => { + logFunctionInfo(nonMemoized, nonMemoized.recomputations()) + }, + } + }, + }, + ) +}) + +// This benchmark is made to test to see at what point it becomes beneficial +// to use reselect to memoize a function that is a plain field accessor. +describe('Reselect vs standalone memoization for field access', () => { + const store = setupStore() + const runSelector = (selector: Selector) => { + runMultipleTimes(selector, 1_000_000, store.getState()) + } + const commonOptions: Options = { + // warmupIterations: 0, + // warmupTime: 0, + // iterations: 10, + // time: 0 + } + const fieldAccessorWithReselect = createSelector( + [(state: RootState) => state.users], + users => users.appSettings, + ) + const fieldAccessorWithMemoize = countRecomputations( + lruMemoize((state: RootState) => { + return state.users.appSettings + }), + ) + const nonMemoizedAccessor = countRecomputations( + (state: RootState) => state.users.appSettings, + ) + + setFunctionNames({ + fieldAccessorWithReselect, + fieldAccessorWithMemoize, + nonMemoizedAccessor, + }) + const createOptions = ( + selector: S, + commonOptions: Options = {}, + ) => { + const options: Options = { + setup: (task, mode) => { + if (mode === 'warmup') return + resetSelector(selector) + task.opts = { + beforeEach: () => { + store.dispatch(toggleCompleted(1)) + }, + afterAll: () => { + logSelectorRecomputations(selector) + }, + } + }, + } + return { ...commonOptions, ...options } + } + bench( + fieldAccessorWithReselect, + () => { + runSelector(fieldAccessorWithReselect) + }, + createOptions(fieldAccessorWithReselect, commonOptions), + ) + bench( + fieldAccessorWithMemoize, + () => { + runSelector(fieldAccessorWithMemoize) + }, + { + ...commonOptions, + setup: (task, mode) => { + if (mode === 'warmup') return + fieldAccessorWithMemoize.resetRecomputations() + fieldAccessorWithMemoize.clearCache() + task.opts = { + beforeEach: () => { + store.dispatch(toggleCompleted(1)) + }, + afterAll: () => { + logFunctionInfo( + fieldAccessorWithMemoize, + fieldAccessorWithMemoize.recomputations(), + ) + }, + } + }, + }, + ) + bench( + nonMemoizedAccessor, + () => { + runSelector(nonMemoizedAccessor) + }, + { + ...commonOptions, + setup: (task, mode) => { + if (mode === 'warmup') return + nonMemoizedAccessor.resetRecomputations() + task.opts = { + beforeEach: () => { + store.dispatch(toggleCompleted(1)) + }, + afterAll: () => { + logFunctionInfo( + nonMemoizedAccessor, + nonMemoizedAccessor.recomputations(), + ) + }, + } + }, + }, + ) +}) diff --git a/test/benchmarks/reselect.bench.ts b/test/benchmarks/reselect.bench.ts index 28d23a94c..0d6e83e4b 100644 --- a/test/benchmarks/reselect.bench.ts +++ b/test/benchmarks/reselect.bench.ts @@ -1,7 +1,7 @@ import { createSelector, unstable_autotrackMemoize as autotrackMemoize, - weakMapMemoize + weakMapMemoize, } from 'reselect' import type { Options } from 'tinybench' import { bench, describe } from 'vitest' @@ -11,43 +11,43 @@ import { setFunctionNames, setupStore } from '../testUtils' describe('Memoize methods comparison', () => { const commonOptions: Options = { iterations: 10, - time: 0 + time: 0, } const store = setupStore() const state = store.getState() const selectorDefault = createSelector( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const selectorWeakMap = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: weakMapMemoize } + { memoize: weakMapMemoize }, ) const selectorAutotrack = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: autotrackMemoize } + { memoize: autotrackMemoize }, ) const selectorArgsWeakMap = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { argsMemoize: weakMapMemoize } + { argsMemoize: weakMapMemoize }, ) const selectorArgsAutotrack = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { argsMemoize: autotrackMemoize } + { argsMemoize: autotrackMemoize }, ) const selectorBothWeakMap = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { argsMemoize: weakMapMemoize, memoize: weakMapMemoize } + { argsMemoize: weakMapMemoize, memoize: weakMapMemoize }, ) const selectorBothAutotrack = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { argsMemoize: autotrackMemoize, memoize: autotrackMemoize } + { argsMemoize: autotrackMemoize, memoize: autotrackMemoize }, ) const nonMemoizedSelector = (state: RootState) => { return state.todos.map(({ id }) => id) @@ -58,70 +58,70 @@ describe('Memoize methods comparison', () => { selectorWeakMap, selectorArgsAutotrack, nonMemoizedSelector, - selectorArgsWeakMap + selectorArgsWeakMap, }) bench( selectorDefault, () => { selectorDefault(state) }, - commonOptions + commonOptions, ) bench( selectorAutotrack, () => { selectorAutotrack(state) }, - commonOptions + commonOptions, ) bench( selectorWeakMap, () => { selectorWeakMap(state) }, - commonOptions + commonOptions, ) bench( selectorArgsAutotrack, () => { selectorArgsAutotrack(state) }, - commonOptions + commonOptions, ) bench( selectorArgsWeakMap, () => { selectorArgsWeakMap(state) }, - commonOptions + commonOptions, ) bench( selectorBothWeakMap, () => { selectorBothWeakMap(state) }, - commonOptions + commonOptions, ) bench( selectorBothAutotrack, () => { selectorBothAutotrack(state) }, - commonOptions + commonOptions, ) bench( nonMemoizedSelector, () => { nonMemoizedSelector(state) }, - commonOptions + commonOptions, ) }) describe('Cached vs non-cached length in for loops', () => { const commonOptions: Options = { iterations: 10, - time: 0 + time: 0, } const store = setupStore() const state = store.getState() @@ -135,7 +135,7 @@ describe('Cached vs non-cached length in for loops', () => { todos[i].id } }, - commonOptions + commonOptions, ) bench( 'length cached', @@ -145,7 +145,7 @@ describe('Cached vs non-cached length in for loops', () => { todos[i].id } }, - commonOptions + commonOptions, ) bench( 'length and arg cached', @@ -156,24 +156,24 @@ describe('Cached vs non-cached length in for loops', () => { arg.id } }, - commonOptions + commonOptions, ) }) describe.todo('nested field access', () => { const commonOptions: Options = { iterations: 10, - time: 0 + time: 0, } const store = setupStore() const state = store.getState() const selectorDefault = createSelector( (state: RootState) => state.users, - users => users.user.details.preferences.notifications.push.frequency + users => users.user.details.preferences.notifications.push.frequency, ) const selectorDefault1 = createSelector( (state: RootState) => state.users.user, - user => user.details.preferences.notifications.push.frequency + user => user.details.preferences.notifications.push.frequency, ) const nonMemoizedSelector = (state: RootState) => state.users.user.details.preferences.notifications.push.frequency @@ -182,38 +182,38 @@ describe.todo('nested field access', () => { () => { selectorDefault(state) }, - commonOptions + commonOptions, ) bench( 'nonMemoizedSelector', () => { nonMemoizedSelector(state) }, - commonOptions + commonOptions, ) bench( 'selectorDefault1', () => { selectorDefault1(state) }, - commonOptions + commonOptions, ) }) describe.todo('simple field access', () => { const commonOptions: Options = { iterations: 10, - time: 0 + time: 0, } const store = setupStore() const state = store.getState() const selectorDefault = createSelector( (state: RootState) => state.users, - users => users.user.details.preferences.notifications.push.frequency + users => users.user.details.preferences.notifications.push.frequency, ) const selectorDefault1 = createSelector( (state: RootState) => state.users.user, - user => user.details.preferences.notifications.push.frequency + user => user.details.preferences.notifications.push.frequency, ) const selectorDefault2 = createSelector( (state: RootState) => state.users, @@ -241,7 +241,7 @@ describe.todo('simple field access', () => { (state: RootState) => state.users, (state: RootState) => state.users, (state: RootState) => state.users, - users => users.user.details.preferences.notifications.push.frequency + users => users.user.details.preferences.notifications.push.frequency, ) const nonMemoizedSelector = (state: RootState) => state.users.user.details.preferences.notifications.push.frequency @@ -250,40 +250,40 @@ describe.todo('simple field access', () => { () => { selectorDefault(state) }, - commonOptions + commonOptions, ) bench( 'nonMemoizedSelector', () => { nonMemoizedSelector(state) }, - commonOptions + commonOptions, ) bench( 'selectorDefault1', () => { selectorDefault1(state) }, - commonOptions + commonOptions, ) bench( 'selectorDefault2', () => { selectorDefault2(state) }, - commonOptions + commonOptions, ) }) describe.todo('field accessors', () => { const commonOptions: Options = { iterations: 10, - time: 0 + time: 0, } const store = setupStore() const selectorDefault = createSelector( [(state: RootState) => state.users], - users => users.appSettings + users => users.appSettings, ) const nonMemoizedSelector = (state: RootState) => state.users.appSettings setFunctionNames({ selectorDefault, nonMemoizedSelector }) @@ -292,13 +292,13 @@ describe.todo('field accessors', () => { () => { selectorDefault(store.getState()) }, - { ...commonOptions } + { ...commonOptions }, ) bench( nonMemoizedSelector, () => { nonMemoizedSelector(store.getState()) }, - { ...commonOptions } + { ...commonOptions }, ) }) diff --git a/test/benchmarks/resultEqualityCheck.bench.ts b/test/benchmarks/resultEqualityCheck.bench.ts index 76dffc06e..fdeaec24a 100644 --- a/test/benchmarks/resultEqualityCheck.bench.ts +++ b/test/benchmarks/resultEqualityCheck.bench.ts @@ -4,7 +4,7 @@ import { createSelector, lruMemoize, referenceEqualityCheck, - weakMapMemoize + weakMapMemoize, } from 'reselect' import type { Options } from 'tinybench' import { bench } from 'vitest' @@ -13,7 +13,7 @@ import { setFunctionNames, setupStore, toggleCompleted, - type RootState + type RootState, } from '../testUtils' describe('memoize functions performance with resultEqualityCheck set to referenceEqualityCheck vs. without resultEqualityCheck', () => { @@ -24,7 +24,7 @@ describe('memoize functions performance with resultEqualityCheck set to referenc const commonOptions: Options = { iterations: 10_000, - time: 0 + time: 0, } const runSelector = (selector: S) => { @@ -37,7 +37,7 @@ describe('memoize functions performance with resultEqualityCheck set to referenc const selectTodoIdsWeakMap = createAppSelector( [state => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const selectTodoIdsWeakMapWithResultEqualityCheck = createAppSelector( @@ -45,14 +45,14 @@ describe('memoize functions performance with resultEqualityCheck set to referenc todos => todos.map(({ id }) => id), { memoizeOptions: { resultEqualityCheck: referenceEqualityCheck }, - argsMemoizeOptions: { resultEqualityCheck: referenceEqualityCheck } - } + argsMemoizeOptions: { resultEqualityCheck: referenceEqualityCheck }, + }, ) const selectTodoIdsLru = createAppSelector( [state => state.todos], todos => todos.map(({ id }) => id), - { memoize: lruMemoize, argsMemoize: lruMemoize } + { memoize: lruMemoize, argsMemoize: lruMemoize }, ) const selectTodoIdsLruWithResultEqualityCheck = createAppSelector( @@ -62,15 +62,15 @@ describe('memoize functions performance with resultEqualityCheck set to referenc memoize: lruMemoize, memoizeOptions: { resultEqualityCheck: referenceEqualityCheck }, argsMemoize: lruMemoize, - argsMemoizeOptions: { resultEqualityCheck: referenceEqualityCheck } - } + argsMemoizeOptions: { resultEqualityCheck: referenceEqualityCheck }, + }, ) const selectors = { selectTodoIdsWeakMap, selectTodoIdsWeakMapWithResultEqualityCheck, selectTodoIdsLru, - selectTodoIdsLruWithResultEqualityCheck + selectTodoIdsLruWithResultEqualityCheck, } setFunctionNames(selectors) @@ -87,9 +87,9 @@ describe('memoize functions performance with resultEqualityCheck set to referenc afterAll: () => { logSelectorRecomputations(selector) - } + }, } - } + }, } return { ...commonOptions, ...options } } @@ -100,7 +100,7 @@ describe('memoize functions performance with resultEqualityCheck set to referenc () => { runSelector(selector) }, - createOptions(selector) + createOptions(selector), ) }) }) @@ -110,12 +110,12 @@ describe('memoize functions performance with resultEqualityCheck set to referenc const arrayOfNumbers = Array.from( { length: 100_000 }, - (num, index) => index + (num, index) => index, ) const commonOptions: Options = { iterations: 1000, - time: 0 + time: 0, } const runSelector = (selector: S) => { @@ -125,36 +125,36 @@ describe('memoize functions performance with resultEqualityCheck set to referenc } const selectTodoIdsWeakMap = weakMapMemoize((state: RootState) => - state.todos.map(({ id }) => id) + state.todos.map(({ id }) => id), ) const selectTodoIdsWeakMapWithResultEqualityCheck = weakMapMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck: referenceEqualityCheck } + { resultEqualityCheck: referenceEqualityCheck }, ) const selectTodoIdsLru = lruMemoize((state: RootState) => - state.todos.map(({ id }) => id) + state.todos.map(({ id }) => id), ) const selectTodoIdsLruWithResultEqualityCheck = lruMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck: referenceEqualityCheck } + { resultEqualityCheck: referenceEqualityCheck }, ) const memoizedFunctions = { selectTodoIdsWeakMap, selectTodoIdsWeakMapWithResultEqualityCheck, selectTodoIdsLru, - selectTodoIdsLruWithResultEqualityCheck + selectTodoIdsLruWithResultEqualityCheck, } setFunctionNames(memoizedFunctions) const createOptions = < - Func extends AnyFunction & { resultsCount: () => number } + Func extends AnyFunction & { resultsCount: () => number }, >( - memoizedFunction: Func + memoizedFunction: Func, ) => { const options: Options = { setup: (task, mode) => { @@ -168,11 +168,11 @@ describe('memoize functions performance with resultEqualityCheck set to referenc afterAll: () => { console.log( memoizedFunction.name, - memoizedFunction.resultsCount() + memoizedFunction.resultsCount(), ) - } + }, } - } + }, } return { ...commonOptions, ...options } } @@ -183,7 +183,7 @@ describe('memoize functions performance with resultEqualityCheck set to referenc () => { runSelector(memoizedFunction) }, - createOptions(memoizedFunction) + createOptions(memoizedFunction), ) }) }) diff --git a/test/benchmarks/weakMapMemoize.bench.ts b/test/benchmarks/weakMapMemoize.bench.ts index e8dd07fe1..4ff6a765a 100644 --- a/test/benchmarks/weakMapMemoize.bench.ts +++ b/test/benchmarks/weakMapMemoize.bench.ts @@ -1,496 +1,496 @@ -import type { OutputSelector, Selector } from 'reselect' -import { - unstable_autotrackMemoize as autotrackMemoize, - createSelector, - lruMemoize, - weakMapMemoize -} from 'reselect' -import { bench } from 'vitest' -import type { RootState } from '../testUtils' -import { - logSelectorRecomputations, - resetSelector, - setFunctionNames, - setupStore -} from '../testUtils' - -import type { Options } from 'tinybench' - -describe('Parametric selectors: weakMapMemoize vs others', () => { - const store = setupStore() - const state = store.getState() - const arrayOfNumbers = Array.from({ length: 30 }, (num, index) => index) - const commonOptions: Options = { - iterations: 10, - time: 0 - } - const runSelector = (selector: S) => { - arrayOfNumbers.forEach(num => { - selector(state, num) - }) - arrayOfNumbers.forEach(num => { - selector(state, num) - }) - } - const selectorDefault = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id) - ) - const selectorDefaultWithCacheSize = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 30 } } - ) - const selectorDefaultWithArgsCacheSize = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - argsMemoizeOptions: { maxSize: 30 } - } - ) - const selectorDefaultWithBothCacheSize = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: { maxSize: 30 }, - argsMemoizeOptions: { maxSize: 30 } - } - ) - const selectorWeakMap = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id), - { memoize: weakMapMemoize } - ) - const selectorAutotrack = createSelector( - (state: RootState) => state.todos, - (state: RootState, id: number) => id, - (todos, id) => todos.find(todo => todo.id === id), - { memoize: autotrackMemoize } - ) - const selectorArgsAutotrack = createSelector( - (state: RootState) => state.todos, - (state: RootState, id: number) => id, - (todos, id) => todos.find(todo => todo.id === id), - { argsMemoize: autotrackMemoize } - ) - const selectorBothAutotrack = createSelector( - (state: RootState) => state.todos, - (state: RootState, id: number) => id, - (todos, id) => todos.find(todo => todo.id === id), - { argsMemoize: autotrackMemoize, memoize: autotrackMemoize } - ) - const selectorArgsWeakMap = createSelector( - (state: RootState) => state.todos, - (state: RootState, id: number) => id, - (todos, id) => todos.find(todo => todo.id === id), - { argsMemoize: weakMapMemoize } - ) - const selectorBothWeakMap = createSelector( - (state: RootState) => state.todos, - (state: RootState, id: number) => id, - (todos, id) => todos.find(todo => todo.id === id), - { argsMemoize: weakMapMemoize, memoize: weakMapMemoize } - ) - const nonMemoizedSelector = (state: RootState, id: number) => { - return state.todos.find(todo => todo.id === id) - } - setFunctionNames({ - selectorDefault, - selectorDefaultWithCacheSize, - selectorDefaultWithArgsCacheSize, - selectorDefaultWithBothCacheSize, - selectorWeakMap, - selectorArgsWeakMap, - selectorBothWeakMap, - selectorAutotrack, - selectorArgsAutotrack, - selectorBothAutotrack, - nonMemoizedSelector - }) - - const createOptions = ( - selector: S, - commonOptions: Options = {} - ) => { - const options: Options = { - setup: (task, mode) => { - if (mode === 'warmup') return - resetSelector(selector) - task.opts = { - afterAll: () => { - logSelectorRecomputations(selector) - } - } - } - } - return { ...commonOptions, ...options } - } - bench( - selectorDefault, - () => { - runSelector(selectorDefault) - }, - createOptions(selectorDefault, commonOptions) - ) - bench( - selectorDefaultWithCacheSize, - () => { - runSelector(selectorDefaultWithCacheSize) - }, - createOptions(selectorDefaultWithCacheSize, commonOptions) - ) - bench( - selectorDefaultWithArgsCacheSize, - () => { - runSelector(selectorDefaultWithArgsCacheSize) - }, - createOptions(selectorDefaultWithArgsCacheSize, commonOptions) - ) - bench( - selectorDefaultWithBothCacheSize, - () => { - runSelector(selectorDefaultWithBothCacheSize) - }, - createOptions(selectorDefaultWithBothCacheSize, commonOptions) - ) - bench( - selectorWeakMap, - () => { - runSelector(selectorWeakMap) - }, - createOptions(selectorWeakMap, commonOptions) - ) - bench( - selectorArgsWeakMap, - () => { - runSelector(selectorArgsWeakMap) - }, - createOptions(selectorArgsWeakMap, commonOptions) - ) - bench( - selectorBothWeakMap, - () => { - runSelector(selectorBothWeakMap) - }, - createOptions(selectorBothWeakMap, commonOptions) - ) - bench( - selectorAutotrack, - () => { - runSelector(selectorAutotrack) - }, - createOptions(selectorAutotrack, commonOptions) - ) - bench( - selectorArgsAutotrack, - () => { - runSelector(selectorArgsAutotrack) - }, - createOptions(selectorArgsAutotrack, commonOptions) - ) - bench( - selectorBothAutotrack, - () => { - runSelector(selectorBothAutotrack) - }, - createOptions(selectorBothAutotrack, commonOptions) - ) - bench( - nonMemoizedSelector, - () => { - runSelector(nonMemoizedSelector) - }, - { ...commonOptions } - ) -}) - -// describe('weakMapMemoize vs lruMemoize with maxSize', () => { -// const store = setupStore() -// const state = store.getState() -// const arrayOfNumbers = Array.from({ length: 30 }, (num, index) => index) -// const commonOptions: Options = { -// iterations: 10, -// time: 0 -// } -// const runSelector = (selector: S) => { -// arrayOfNumbers.forEach(num => { -// selector(state, num) -// }) -// arrayOfNumbers.forEach(num => { -// selector(state, num) -// }) -// } -// const selectorDefaultWithCacheSize = createSelector( -// [(state: RootState) => state.todos, (state: RootState, id: number) => id], -// (todos, id) => todos.map(todo => todo.id === id), -// { memoizeOptions: { maxSize: 30 } } -// ) -// const selectorDefaultWithArgsCacheSize = createSelector( -// [(state: RootState) => state.todos, (state: RootState, id: number) => id], -// (todos, id) => todos.map(todo => todo.id === id), -// { argsMemoizeOptions: { maxSize: 30 } } -// ) -// const selectorDefaultWithBothCacheSize = createSelector( -// [(state: RootState) => state.todos, (state: RootState, id: number) => id], -// (todos, id) => todos.map(todo => todo.id === id), -// { memoizeOptions: { maxSize: 30 }, argsMemoizeOptions: { maxSize: 30 } } -// ) -// const selectorWeakMap = createSelector( -// [(state: RootState) => state.todos, (state: RootState, id: number) => id], -// (todos, id) => todos.map(todo => todo.id === id), -// { memoize: weakMapMemoize } -// ) -// const selectorArgsWeakMap = createSelector( -// (state: RootState) => state.todos, -// (state: RootState, id: number) => id, -// (todos, id) => todos.map(todo => todo.id === id), -// { argsMemoize: weakMapMemoize } -// ) -// const selectorBothWeakMap = createSelector( -// (state: RootState) => state.todos, -// (state: RootState, id: number) => id, -// (todos, id) => todos.map(todo => todo.id === id), -// { argsMemoize: weakMapMemoize, memoize: weakMapMemoize } -// ) -// const nonMemoizedSelector = (state: RootState, id: number) => { -// return state.todos.map(todo => todo.id === id) -// } -// setFunctionNames({ -// selectorDefaultWithCacheSize, -// selectorDefaultWithArgsCacheSize, -// selectorDefaultWithBothCacheSize, -// selectorWeakMap, -// selectorArgsWeakMap, -// selectorBothWeakMap, -// nonMemoizedSelector -// }) -// const createOptions = ( -// selector: S, -// commonOptions: Options = {} -// ) => { -// const options: Options = { -// setup: (task, mode) => { -// if (mode === 'warmup') return -// resetSelector(selector) -// task.opts = { -// afterAll: () => { -// logSelectorRecomputations(selector) -// } -// } -// } -// } -// return { ...commonOptions, ...options } -// } -// bench( -// selectorDefaultWithCacheSize, -// () => { -// runSelector(selectorDefaultWithCacheSize) -// }, -// createOptions(selectorDefaultWithCacheSize, commonOptions) -// ) -// bench( -// selectorDefaultWithArgsCacheSize, -// () => { -// runSelector(selectorDefaultWithArgsCacheSize) -// }, -// createOptions(selectorDefaultWithArgsCacheSize, commonOptions) -// ) -// bench( -// selectorDefaultWithBothCacheSize, -// () => { -// runSelector(selectorDefaultWithBothCacheSize) -// }, -// createOptions(selectorDefaultWithBothCacheSize, commonOptions) -// ) -// bench( -// selectorWeakMap, -// () => { -// runSelector(selectorWeakMap) -// }, -// createOptions(selectorWeakMap, commonOptions) -// ) -// bench( -// selectorArgsWeakMap, -// () => { -// runSelector(selectorArgsWeakMap) -// }, -// createOptions(selectorArgsWeakMap, commonOptions) -// ) -// bench( -// selectorBothWeakMap, -// () => { -// runSelector(selectorBothWeakMap) -// }, -// createOptions(selectorBothWeakMap, commonOptions) -// ) -// bench( -// nonMemoizedSelector, -// () => { -// runSelector(nonMemoizedSelector) -// }, -// { ...commonOptions } -// ) -// }) - -describe('Simple selectors: weakMapMemoize vs others', () => { - const store = setupStore() - const commonOptions: Options = { - // warmupIterations: 0, - // warmupTime: 0, - // iterations: 10, - // time: 0 - } - const selectTodoIdsDefault = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) - ) - const selectTodoIdsWeakMap = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id), - { argsMemoize: weakMapMemoize } - ) - const selectTodoIdsAutotrack = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id), - { memoize: autotrackMemoize } - ) - - setFunctionNames({ - selectTodoIdsDefault, - selectTodoIdsWeakMap, - selectTodoIdsAutotrack - }) - - const createOptions = (selector: S) => { - const options: Options = { - setup: (task, mode) => { - if (mode === 'warmup') return - resetSelector(selector) - task.opts = { - afterAll: () => { - logSelectorRecomputations(selector) - } - } - } - } - return { ...commonOptions, ...options } - } - - bench( - selectTodoIdsDefault, - () => { - selectTodoIdsDefault(store.getState()) - }, - createOptions(selectTodoIdsDefault) - ) - bench( - selectTodoIdsWeakMap, - () => { - selectTodoIdsWeakMap(store.getState()) - }, - createOptions(selectTodoIdsWeakMap) - ) - bench( - selectTodoIdsAutotrack, - () => { - selectTodoIdsAutotrack(store.getState()) - }, - createOptions(selectTodoIdsAutotrack) - ) -}) - -describe.skip('weakMapMemoize memory leak', () => { - const store = setupStore() - const state = store.getState() - const arrayOfNumbers = Array.from( - { length: 2_000_000 }, - (num, index) => index - ) - const commonOptions: Options = { - warmupIterations: 0, - warmupTime: 0, - iterations: 1, - time: 0 - } - const runSelector = (selector: S) => { - arrayOfNumbers.forEach(num => { - selector(state, num) - }) - arrayOfNumbers.forEach(num => { - selector(state, num) - }) - } - const selectorDefault = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - todos => todos.map(({ id }) => id) - ) - const selectorWeakMap = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - todos => todos.map(({ id }) => id), - { memoize: weakMapMemoize } - ) - const selectorArgsWeakMap = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - todos => todos.map(({ id }) => id), - { argsMemoize: weakMapMemoize } - ) - const selectorBothWeakMap = createSelector( - [(state: RootState) => state.todos, (state: RootState, id: number) => id], - todos => todos.map(({ id }) => id), - { argsMemoize: weakMapMemoize, memoize: weakMapMemoize } - ) - setFunctionNames({ - selectorDefault, - selectorWeakMap, - selectorArgsWeakMap, - selectorBothWeakMap - }) - const createOptions = ( - selector: S, - commonOptions: Options = {} - ) => { - const options: Options = { - setup: (task, mode) => { - if (mode === 'warmup') return - task.opts = { - afterAll: () => { - logSelectorRecomputations(selector) - } - } - } - } - return { ...commonOptions, ...options } - } - bench( - selectorDefault, - () => { - runSelector(selectorDefault) - }, - createOptions(selectorDefault, commonOptions) - ) - bench( - selectorWeakMap, - () => { - runSelector(selectorWeakMap) - }, - createOptions(selectorWeakMap, commonOptions) - ) - bench.skip( - selectorArgsWeakMap, - () => { - runSelector(selectorArgsWeakMap) - }, - createOptions(selectorArgsWeakMap, commonOptions) - ) - bench.skip( - selectorBothWeakMap, - () => { - runSelector(selectorBothWeakMap) - }, - createOptions(selectorBothWeakMap, commonOptions) - ) -}) +import type { OutputSelector, Selector } from 'reselect' +import { + unstable_autotrackMemoize as autotrackMemoize, + createSelector, + lruMemoize, + weakMapMemoize, +} from 'reselect' +import { bench } from 'vitest' +import type { RootState } from '../testUtils' +import { + logSelectorRecomputations, + resetSelector, + setFunctionNames, + setupStore, +} from '../testUtils' + +import type { Options } from 'tinybench' + +describe('Parametric selectors: weakMapMemoize vs others', () => { + const store = setupStore() + const state = store.getState() + const arrayOfNumbers = Array.from({ length: 30 }, (num, index) => index) + const commonOptions: Options = { + iterations: 10, + time: 0, + } + const runSelector = (selector: S) => { + arrayOfNumbers.forEach(num => { + selector(state, num) + }) + arrayOfNumbers.forEach(num => { + selector(state, num) + }) + } + const selectorDefault = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id), + ) + const selectorDefaultWithCacheSize = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 30 } }, + ) + const selectorDefaultWithArgsCacheSize = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + argsMemoizeOptions: { maxSize: 30 }, + }, + ) + const selectorDefaultWithBothCacheSize = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: { maxSize: 30 }, + argsMemoizeOptions: { maxSize: 30 }, + }, + ) + const selectorWeakMap = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + (todos, id) => todos.find(todo => todo.id === id), + { memoize: weakMapMemoize }, + ) + const selectorAutotrack = createSelector( + (state: RootState) => state.todos, + (state: RootState, id: number) => id, + (todos, id) => todos.find(todo => todo.id === id), + { memoize: autotrackMemoize }, + ) + const selectorArgsAutotrack = createSelector( + (state: RootState) => state.todos, + (state: RootState, id: number) => id, + (todos, id) => todos.find(todo => todo.id === id), + { argsMemoize: autotrackMemoize }, + ) + const selectorBothAutotrack = createSelector( + (state: RootState) => state.todos, + (state: RootState, id: number) => id, + (todos, id) => todos.find(todo => todo.id === id), + { argsMemoize: autotrackMemoize, memoize: autotrackMemoize }, + ) + const selectorArgsWeakMap = createSelector( + (state: RootState) => state.todos, + (state: RootState, id: number) => id, + (todos, id) => todos.find(todo => todo.id === id), + { argsMemoize: weakMapMemoize }, + ) + const selectorBothWeakMap = createSelector( + (state: RootState) => state.todos, + (state: RootState, id: number) => id, + (todos, id) => todos.find(todo => todo.id === id), + { argsMemoize: weakMapMemoize, memoize: weakMapMemoize }, + ) + const nonMemoizedSelector = (state: RootState, id: number) => { + return state.todos.find(todo => todo.id === id) + } + setFunctionNames({ + selectorDefault, + selectorDefaultWithCacheSize, + selectorDefaultWithArgsCacheSize, + selectorDefaultWithBothCacheSize, + selectorWeakMap, + selectorArgsWeakMap, + selectorBothWeakMap, + selectorAutotrack, + selectorArgsAutotrack, + selectorBothAutotrack, + nonMemoizedSelector, + }) + + const createOptions = ( + selector: S, + commonOptions: Options = {}, + ) => { + const options: Options = { + setup: (task, mode) => { + if (mode === 'warmup') return + resetSelector(selector) + task.opts = { + afterAll: () => { + logSelectorRecomputations(selector) + }, + } + }, + } + return { ...commonOptions, ...options } + } + bench( + selectorDefault, + () => { + runSelector(selectorDefault) + }, + createOptions(selectorDefault, commonOptions), + ) + bench( + selectorDefaultWithCacheSize, + () => { + runSelector(selectorDefaultWithCacheSize) + }, + createOptions(selectorDefaultWithCacheSize, commonOptions), + ) + bench( + selectorDefaultWithArgsCacheSize, + () => { + runSelector(selectorDefaultWithArgsCacheSize) + }, + createOptions(selectorDefaultWithArgsCacheSize, commonOptions), + ) + bench( + selectorDefaultWithBothCacheSize, + () => { + runSelector(selectorDefaultWithBothCacheSize) + }, + createOptions(selectorDefaultWithBothCacheSize, commonOptions), + ) + bench( + selectorWeakMap, + () => { + runSelector(selectorWeakMap) + }, + createOptions(selectorWeakMap, commonOptions), + ) + bench( + selectorArgsWeakMap, + () => { + runSelector(selectorArgsWeakMap) + }, + createOptions(selectorArgsWeakMap, commonOptions), + ) + bench( + selectorBothWeakMap, + () => { + runSelector(selectorBothWeakMap) + }, + createOptions(selectorBothWeakMap, commonOptions), + ) + bench( + selectorAutotrack, + () => { + runSelector(selectorAutotrack) + }, + createOptions(selectorAutotrack, commonOptions), + ) + bench( + selectorArgsAutotrack, + () => { + runSelector(selectorArgsAutotrack) + }, + createOptions(selectorArgsAutotrack, commonOptions), + ) + bench( + selectorBothAutotrack, + () => { + runSelector(selectorBothAutotrack) + }, + createOptions(selectorBothAutotrack, commonOptions), + ) + bench( + nonMemoizedSelector, + () => { + runSelector(nonMemoizedSelector) + }, + { ...commonOptions }, + ) +}) + +// describe('weakMapMemoize vs lruMemoize with maxSize', () => { +// const store = setupStore() +// const state = store.getState() +// const arrayOfNumbers = Array.from({ length: 30 }, (num, index) => index) +// const commonOptions: Options = { +// iterations: 10, +// time: 0 +// } +// const runSelector = (selector: S) => { +// arrayOfNumbers.forEach(num => { +// selector(state, num) +// }) +// arrayOfNumbers.forEach(num => { +// selector(state, num) +// }) +// } +// const selectorDefaultWithCacheSize = createSelector( +// [(state: RootState) => state.todos, (state: RootState, id: number) => id], +// (todos, id) => todos.map(todo => todo.id === id), +// { memoizeOptions: { maxSize: 30 } } +// ) +// const selectorDefaultWithArgsCacheSize = createSelector( +// [(state: RootState) => state.todos, (state: RootState, id: number) => id], +// (todos, id) => todos.map(todo => todo.id === id), +// { argsMemoizeOptions: { maxSize: 30 } } +// ) +// const selectorDefaultWithBothCacheSize = createSelector( +// [(state: RootState) => state.todos, (state: RootState, id: number) => id], +// (todos, id) => todos.map(todo => todo.id === id), +// { memoizeOptions: { maxSize: 30 }, argsMemoizeOptions: { maxSize: 30 } } +// ) +// const selectorWeakMap = createSelector( +// [(state: RootState) => state.todos, (state: RootState, id: number) => id], +// (todos, id) => todos.map(todo => todo.id === id), +// { memoize: weakMapMemoize } +// ) +// const selectorArgsWeakMap = createSelector( +// (state: RootState) => state.todos, +// (state: RootState, id: number) => id, +// (todos, id) => todos.map(todo => todo.id === id), +// { argsMemoize: weakMapMemoize } +// ) +// const selectorBothWeakMap = createSelector( +// (state: RootState) => state.todos, +// (state: RootState, id: number) => id, +// (todos, id) => todos.map(todo => todo.id === id), +// { argsMemoize: weakMapMemoize, memoize: weakMapMemoize } +// ) +// const nonMemoizedSelector = (state: RootState, id: number) => { +// return state.todos.map(todo => todo.id === id) +// } +// setFunctionNames({ +// selectorDefaultWithCacheSize, +// selectorDefaultWithArgsCacheSize, +// selectorDefaultWithBothCacheSize, +// selectorWeakMap, +// selectorArgsWeakMap, +// selectorBothWeakMap, +// nonMemoizedSelector +// }) +// const createOptions = ( +// selector: S, +// commonOptions: Options = {} +// ) => { +// const options: Options = { +// setup: (task, mode) => { +// if (mode === 'warmup') return +// resetSelector(selector) +// task.opts = { +// afterAll: () => { +// logSelectorRecomputations(selector) +// } +// } +// } +// } +// return { ...commonOptions, ...options } +// } +// bench( +// selectorDefaultWithCacheSize, +// () => { +// runSelector(selectorDefaultWithCacheSize) +// }, +// createOptions(selectorDefaultWithCacheSize, commonOptions) +// ) +// bench( +// selectorDefaultWithArgsCacheSize, +// () => { +// runSelector(selectorDefaultWithArgsCacheSize) +// }, +// createOptions(selectorDefaultWithArgsCacheSize, commonOptions) +// ) +// bench( +// selectorDefaultWithBothCacheSize, +// () => { +// runSelector(selectorDefaultWithBothCacheSize) +// }, +// createOptions(selectorDefaultWithBothCacheSize, commonOptions) +// ) +// bench( +// selectorWeakMap, +// () => { +// runSelector(selectorWeakMap) +// }, +// createOptions(selectorWeakMap, commonOptions) +// ) +// bench( +// selectorArgsWeakMap, +// () => { +// runSelector(selectorArgsWeakMap) +// }, +// createOptions(selectorArgsWeakMap, commonOptions) +// ) +// bench( +// selectorBothWeakMap, +// () => { +// runSelector(selectorBothWeakMap) +// }, +// createOptions(selectorBothWeakMap, commonOptions) +// ) +// bench( +// nonMemoizedSelector, +// () => { +// runSelector(nonMemoizedSelector) +// }, +// { ...commonOptions } +// ) +// }) + +describe('Simple selectors: weakMapMemoize vs others', () => { + const store = setupStore() + const commonOptions: Options = { + // warmupIterations: 0, + // warmupTime: 0, + // iterations: 10, + // time: 0 + } + const selectTodoIdsDefault = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(({ id }) => id), + ) + const selectTodoIdsWeakMap = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(({ id }) => id), + { argsMemoize: weakMapMemoize }, + ) + const selectTodoIdsAutotrack = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(({ id }) => id), + { memoize: autotrackMemoize }, + ) + + setFunctionNames({ + selectTodoIdsDefault, + selectTodoIdsWeakMap, + selectTodoIdsAutotrack, + }) + + const createOptions = (selector: S) => { + const options: Options = { + setup: (task, mode) => { + if (mode === 'warmup') return + resetSelector(selector) + task.opts = { + afterAll: () => { + logSelectorRecomputations(selector) + }, + } + }, + } + return { ...commonOptions, ...options } + } + + bench( + selectTodoIdsDefault, + () => { + selectTodoIdsDefault(store.getState()) + }, + createOptions(selectTodoIdsDefault), + ) + bench( + selectTodoIdsWeakMap, + () => { + selectTodoIdsWeakMap(store.getState()) + }, + createOptions(selectTodoIdsWeakMap), + ) + bench( + selectTodoIdsAutotrack, + () => { + selectTodoIdsAutotrack(store.getState()) + }, + createOptions(selectTodoIdsAutotrack), + ) +}) + +describe.skip('weakMapMemoize memory leak', () => { + const store = setupStore() + const state = store.getState() + const arrayOfNumbers = Array.from( + { length: 2_000_000 }, + (num, index) => index, + ) + const commonOptions: Options = { + warmupIterations: 0, + warmupTime: 0, + iterations: 1, + time: 0, + } + const runSelector = (selector: S) => { + arrayOfNumbers.forEach(num => { + selector(state, num) + }) + arrayOfNumbers.forEach(num => { + selector(state, num) + }) + } + const selectorDefault = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + todos => todos.map(({ id }) => id), + ) + const selectorWeakMap = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + todos => todos.map(({ id }) => id), + { memoize: weakMapMemoize }, + ) + const selectorArgsWeakMap = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + todos => todos.map(({ id }) => id), + { argsMemoize: weakMapMemoize }, + ) + const selectorBothWeakMap = createSelector( + [(state: RootState) => state.todos, (state: RootState, id: number) => id], + todos => todos.map(({ id }) => id), + { argsMemoize: weakMapMemoize, memoize: weakMapMemoize }, + ) + setFunctionNames({ + selectorDefault, + selectorWeakMap, + selectorArgsWeakMap, + selectorBothWeakMap, + }) + const createOptions = ( + selector: S, + commonOptions: Options = {}, + ) => { + const options: Options = { + setup: (task, mode) => { + if (mode === 'warmup') return + task.opts = { + afterAll: () => { + logSelectorRecomputations(selector) + }, + } + }, + } + return { ...commonOptions, ...options } + } + bench( + selectorDefault, + () => { + runSelector(selectorDefault) + }, + createOptions(selectorDefault, commonOptions), + ) + bench( + selectorWeakMap, + () => { + runSelector(selectorWeakMap) + }, + createOptions(selectorWeakMap, commonOptions), + ) + bench.skip( + selectorArgsWeakMap, + () => { + runSelector(selectorArgsWeakMap) + }, + createOptions(selectorArgsWeakMap, commonOptions), + ) + bench.skip( + selectorBothWeakMap, + () => { + runSelector(selectorBothWeakMap) + }, + createOptions(selectorBothWeakMap, commonOptions), + ) +}) diff --git a/test/computationComparisons.spec.tsx b/test/computationComparisons.spec.tsx index 472080f05..e397fe50f 100644 --- a/test/computationComparisons.spec.tsx +++ b/test/computationComparisons.spec.tsx @@ -10,7 +10,7 @@ import { createSelector, lruMemoize, unstable_autotrackMemoize, - weakMapMemoize + weakMapMemoize, } from 'reselect' import type { OutputSelector } from 'reselect' @@ -20,7 +20,7 @@ import { addTodo, setupStore, toggleCompleted } from './testUtils' describe('Computations and re-rendering with React components', () => { const selector = createSelector( (a: number) => a, - a => a + a => a, ) test('passes', () => { @@ -46,7 +46,7 @@ describe('Computations and re-rendering with React components', () => { type SelectTodoById = OutputSelector< [ (state: RootState) => RootState['todos'], - (state: RootState, id: number) => number + (state: RootState, id: number) => number, ], readonly [todo: Todo | undefined], typeof lruMemoize | typeof weakMapMemoize, @@ -67,12 +67,12 @@ describe('Computations and re-rendering with React components', () => { const selectTodoIdsResultEquality = createSelector( [selectTodos], mapTodoIds, - { memoizeOptions: { resultEqualityCheck: shallowEqual } } + { memoizeOptions: { resultEqualityCheck: shallowEqual } }, ) const selectTodoIdsWeakMap = createSelector([selectTodos], mapTodoIds, { argsMemoize: weakMapMemoize, - memoize: weakMapMemoize + memoize: weakMapMemoize, }) const selectTodoIdsWeakMapResultEquality = createSelector( @@ -81,13 +81,13 @@ describe('Computations and re-rendering with React components', () => { { argsMemoize: weakMapMemoize, memoize: weakMapMemoize, - memoizeOptions: { resultEqualityCheck: shallowEqual } - } + memoizeOptions: { resultEqualityCheck: shallowEqual }, + }, ) const selectTodoByIdDefault = createSelector( [selectTodos, selectTodoId], - mapTodoById + mapTodoById, ) const selectTodoByIdResultEquality = createSelector( @@ -95,14 +95,14 @@ describe('Computations and re-rendering with React components', () => { mapTodoById, { memoize: lruMemoize, - memoizeOptions: { resultEqualityCheck: shallowEqual, maxSize: 500 } - } + memoizeOptions: { resultEqualityCheck: shallowEqual, maxSize: 500 }, + }, ) const selectTodoByIdWeakMap = createSelector( [selectTodos, selectTodoId], mapTodoById, - { argsMemoize: weakMapMemoize, memoize: weakMapMemoize } + { argsMemoize: weakMapMemoize, memoize: weakMapMemoize }, ) const useAppSelector: TypedUseSelectorHook = useSelector @@ -113,7 +113,7 @@ describe('Computations and re-rendering with React components', () => { const TodoListItem = React.memo(function TodoListItem({ id, - selectTodoById + selectTodoById, }: { id: number selectTodoById: SelectTodoById @@ -122,7 +122,7 @@ describe('Computations and re-rendering with React components', () => { // due to passing in a new selector reference const memoizedSelectTodoById = useMemo( () => (state: RootState) => selectTodoById(state, id), - [id] + [id], ) const [todo] = useAppSelector(memoizedSelectTodoById) @@ -139,7 +139,7 @@ describe('Computations and re-rendering with React components', () => { const TodoList = ({ selectTodoIds, - selectTodoById + selectTodoById, }: { selectTodoIds: SelectTodoIds selectTodoById: SelectTodoById @@ -164,15 +164,15 @@ describe('Computations and re-rendering with React components', () => { [ 'resultEquality', selectTodoIdsResultEquality, - selectTodoByIdResultEquality + selectTodoByIdResultEquality, ], ['weakMap', selectTodoIdsWeakMap, selectTodoByIdWeakMap], [ 'weakMapResultEquality', selectTodoIdsWeakMapResultEquality, - selectTodoByIdWeakMap - ] + selectTodoByIdWeakMap, + ], ] test.each(testCases)(`%s`, async (name, selectTodoIds, selectTodoById) => { @@ -190,7 +190,7 @@ describe('Computations and re-rendering with React components', () => { selectTodoIds={selectTodoIds} selectTodoById={selectTodoById} /> - + , ) // console.log(`Recomputations after render (${name}): `) @@ -248,20 +248,20 @@ describe('resultEqualityCheck in weakMapMemoize', () => { const selectorWeakMap = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: weakMapMemoize } + { memoize: weakMapMemoize }, ) const selectorWeakMapShallow = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), { memoize: weakMapMemoize, - memoizeOptions: { resultEqualityCheck: shallowEqual } - } + memoizeOptions: { resultEqualityCheck: shallowEqual }, + }, ) const selectorAutotrack = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: unstable_autotrackMemoize } + { memoize: unstable_autotrackMemoize }, ) const firstResult = selectorWeakMap(store.getState()) store.dispatch(toggleCompleted(0)) @@ -278,11 +278,11 @@ describe('resultEqualityCheck in weakMapMemoize', () => { expect(firstResultAutotrack).toBe(secondResultAutotrack) const memoized = weakMapMemoize((state: RootState) => - state.todos.map(({ id }) => id) + state.todos.map(({ id }) => id), ) const memoizedShallow = weakMapMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck: shallowEqual } + { resultEqualityCheck: shallowEqual }, ) expect(memoized.resetResultsCount).to.be.a('function') expect(memoized.resultsCount).to.be.a('function') diff --git a/test/createSelector.withTypes.test.ts b/test/createSelector.withTypes.test.ts index 2cf3e7b99..760440b4c 100644 --- a/test/createSelector.withTypes.test.ts +++ b/test/createSelector.withTypes.test.ts @@ -13,7 +13,7 @@ describe(createSelector.withTypes, () => { expect(createTypedSelector).toBe(createSelector) const selectTodoIds = createTypedSelector([state => state.todos], todos => - todos.map(({ id }) => id) + todos.map(({ id }) => id), ) expect(selectTodoIds).toBeMemoizedSelector() diff --git a/test/createStructuredSelector.spec.ts b/test/createStructuredSelector.spec.ts index ddeb2a048..0cfb99835 100644 --- a/test/createStructuredSelector.spec.ts +++ b/test/createStructuredSelector.spec.ts @@ -2,7 +2,7 @@ import { createSelector, createSelectorCreator, createStructuredSelector, - lruMemoize + lruMemoize, } from 'reselect' import type { LocalTestContext, RootState } from './testUtils' import { setupStore } from './testUtils' @@ -16,7 +16,7 @@ describe(createStructuredSelector, () => { test('structured selector', () => { const selector = createStructuredSelector({ x: (state: StateAB) => state.a, - y: (state: StateAB) => state.b + y: (state: StateAB) => state.b, }) const firstResult = selector({ a: 1, b: 2 }) expect(firstResult).toEqual({ x: 1, y: 2 }) @@ -31,31 +31,31 @@ describe(createStructuredSelector, () => { createStructuredSelector( // @ts-expect-error (state: StateAB) => state.a, - (state: StateAB) => state.b - ) + (state: StateAB) => state.b, + ), ).toThrow(/expects first argument to be an object.*function/) expect(() => createStructuredSelector({ a: state => state.b, // @ts-expect-error - c: 'd' - }) + c: 'd', + }), ).toThrow( - 'createSelector expects all input-selectors to be functions, but received the following types: [function a(), string]' + 'createSelector expects all input-selectors to be functions, but received the following types: [function a(), string]', ) }) test('structured selector with custom selector creator', () => { const customSelectorCreator = createSelectorCreator( lruMemoize, - (a, b) => a === b + (a, b) => a === b, ) const selector = createStructuredSelector( { x: (state: StateAB) => state.a, - y: (state: StateAB) => state.b + y: (state: StateAB) => state.b, }, - customSelectorCreator + customSelectorCreator, ) const firstResult = selector({ a: 1, b: 2 }) expect(firstResult).toEqual({ x: 1, y: 2 }) @@ -77,56 +77,60 @@ describe('structured selector created with createStructuredSel { allTodos: (state: RootState) => state.todos, allAlerts: (state: RootState) => state.alerts, - selectedTodo: (state: RootState, id: number) => state.todos[id] + selectedTodo: (state: RootState, id: number) => state.todos[id], }, - createSelector + createSelector, ) const selector = createSelector( [ (state: RootState) => state.todos, (state: RootState) => state.alerts, - (state: RootState, id: number) => state.todos[id] + (state: RootState, id: number) => state.todos[id], ], (allTodos, allAlerts, selectedTodo) => { return { allTodos, allAlerts, - selectedTodo + selectedTodo, } - } + }, ) expect(selector(state, 1).selectedTodo.id).toBe( - structuredSelector(state, 1).selectedTodo.id + structuredSelector(state, 1).selectedTodo.id, ) expect(structuredSelector.dependencies) .to.be.an('array') .with.lengthOf(selector.dependencies.length) expect( - structuredSelector.resultFunc(state.todos, state.alerts, state.todos[0]) + structuredSelector.resultFunc( + state.todos, + state.alerts, + state.todos[0], + ), ).toStrictEqual( - selector.resultFunc(state.todos, state.alerts, state.todos[0]) + selector.resultFunc(state.todos, state.alerts, state.todos[0]), ) expect( structuredSelector.memoizedResultFunc( state.todos, state.alerts, - state.todos[0] - ) + state.todos[0], + ), ).toStrictEqual( - selector.memoizedResultFunc(state.todos, state.alerts, state.todos[0]) + selector.memoizedResultFunc(state.todos, state.alerts, state.todos[0]), ) expect(structuredSelector.argsMemoize).toBe(selector.argsMemoize) expect(structuredSelector.memoize).toBe(selector.memoize) expect(structuredSelector.recomputations()).toBe( - selector.recomputations() + selector.recomputations(), ) expect(structuredSelector.lastResult()).toStrictEqual( - selector.lastResult() + selector.lastResult(), ) expect(Object.keys(structuredSelector)).toStrictEqual( - Object.keys(selector) + Object.keys(selector), ) - } + }, ) localTest( @@ -139,10 +143,10 @@ describe('structured selector created with createStructuredSel selectedTodo: ( state: RootState, id: number, - field: keyof RootState['todos'][number] - ) => state.todos[id][field] + field: keyof RootState['todos'][number], + ) => state.todos[id][field], }, - createSelector + createSelector, ) const selector = createSelector( [ @@ -151,22 +155,22 @@ describe('structured selector created with createStructuredSel ( state: RootState, id: number, - field: keyof RootState['todos'][number] - ) => state.todos[id][field] + field: keyof RootState['todos'][number], + ) => state.todos[id][field], ], (allTodos, allAlerts, selectedTodo) => { return { allTodos, allAlerts, - selectedTodo + selectedTodo, } - } + }, ) // These two cases are the same. // @ts-expect-error expect(() => structuredSelector(state)).toThrowError(TypeError) // @ts-expect-error expect(() => selector(state)).toThrowError(TypeError) - } + }, ) }) diff --git a/test/createStructuredSelector.withTypes.test.ts b/test/createStructuredSelector.withTypes.test.ts index bcdb30816..4e0824d72 100644 --- a/test/createStructuredSelector.withTypes.test.ts +++ b/test/createStructuredSelector.withTypes.test.ts @@ -10,14 +10,14 @@ describe(createStructuredSelector.withTypes, () => { expect(createTypedStructuredSelector.withTypes).to.be.a('function') expect(createTypedStructuredSelector.withTypes().withTypes).to.be.a( - 'function' + 'function', ) expect(createTypedStructuredSelector).toBe(createStructuredSelector) const structuredSelector = createTypedStructuredSelector({ todos: state => state.todos, - alerts: state => state.alerts + alerts: state => state.alerts, }) expect(structuredSelector).toBeMemoizedSelector() diff --git a/test/examples.test.ts b/test/examples.test.ts index 52132c941..b7a9b81fa 100644 --- a/test/examples.test.ts +++ b/test/examples.test.ts @@ -2,14 +2,14 @@ import type { OutputSelector, Selector, SelectorArray, - UnknownMemoizer + UnknownMemoizer, } from 'reselect' import { unstable_autotrackMemoize as autotrackMemoize, createSelector, createSelectorCreator, lruMemoize, - weakMapMemoize + weakMapMemoize, } from 'reselect' import { test } from 'vitest' import type { RootState } from './testUtils' @@ -27,7 +27,7 @@ const selectCompletedTodos = createSelector( [(state: RootState) => state.todos], todos => { return fallbackToEmptyArray(todos.filter(todo => todo.completed === true)) - } + }, ) const completedTodos = selectCompletedTodos(store.getState()) @@ -42,12 +42,12 @@ test('identity', () => { const identity = any>(func: Func) => func const createNonMemoizedSelector = createSelectorCreator({ memoize: identity, - argsMemoize: identity + argsMemoize: identity, }) const nonMemoizedSelector = createNonMemoizedSelector( [(state: RootState) => state.todos], todos => todos.filter(todo => todo.completed === true), - { devModeChecks: { inputStabilityCheck: 'never' } } + { devModeChecks: { inputStabilityCheck: 'never' } }, ) nonMemoizedSelector(store.getState()) @@ -67,7 +67,7 @@ test.todo('Top Level Selectors', () => { const topLevelSelectors: TopLevelSelectors = { selectAlerts: state => state.alerts, selectTodos: state => state.todos, - selectUsers: state => state.users + selectUsers: state => state.users, } }) @@ -75,7 +75,7 @@ test.todo('Find Fastest Selector', () => { const store = setupStore() const selectTodoIds = createSelector( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const findFastestSelector = ( selector: S, @@ -87,7 +87,7 @@ test.todo('Find Fastest Selector', () => { const alternateSelector = createSelector( selector.dependencies as [...SelectorArray], selector.resultFunc, - { memoize } + { memoize }, ) const start = performance.now() alternateSelector.apply(null, selectorArgs) @@ -96,7 +96,7 @@ test.todo('Find Fastest Selector', () => { }) .sort((a, b) => a.time - b.time) const fastest = results.reduce((minResult, currentResult) => - currentResult.time < minResult.time ? currentResult : minResult + currentResult.time < minResult.time ? currentResult : minResult, ) const ratios = results .filter(({ time }) => time !== fastest.time) @@ -104,7 +104,7 @@ test.todo('Find Fastest Selector', () => { ({ time, name }) => `\x1B[33m \x1B[1m${ time / fastest.time - }\x1B[0m times faster than \x1B[1;41m${name}\x1B[0m.` + }\x1B[0m times faster than \x1B[1;41m${name}\x1B[0m.`, ) if (fastest.selector.memoize.name !== selector.memoize.name) { console.warn( @@ -116,7 +116,7 @@ test.todo('Find Fastest Selector', () => { fastest.selector.memoize.name }\x1B[0m to be more efficient.\nYou should use \x1B[32m\x1B[1m${ fastest.name - }\x1B[0m because it is${ratios.join('\nand\n')}` + }\x1B[0m because it is${ratios.join('\nand\n')}`, ) } return { results, fastest } as const @@ -127,12 +127,12 @@ test('TypedCreateSelector', () => { type TypedCreateSelector< State, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, > = < InputSelectors extends readonly Selector[], Result, OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction, - OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction + OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction, >( ...createSelectorArgs: Parameters< typeof createSelector< @@ -153,7 +153,7 @@ test('TypedCreateSelector', () => { const createAppSelector: TypedCreateSelector = createSelector const selector = createAppSelector( [state => state.todos, (state, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id)?.completed + (todos, id) => todos.find(todo => todo.id === id)?.completed, ) }) @@ -163,9 +163,9 @@ test('createCurriedSelector copy paste pattern', () => { State, Result, Params extends readonly any[], - AdditionalFields + AdditionalFields, >( - selector: ((state: State, ...args: Params) => Result) & AdditionalFields + selector: ((state: State, ...args: Params) => Result) & AdditionalFields, ) => { const curriedSelector = (...args: Params) => { return (state: State) => { @@ -179,7 +179,7 @@ test('createCurriedSelector copy paste pattern', () => { InputSelectors extends SelectorArray, Result, OverrideMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - OverrideArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + OverrideArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, >( ...args: Parameters< typeof createSelector< @@ -194,14 +194,14 @@ test('createCurriedSelector copy paste pattern', () => { } const selectTodoById = createSelector( [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id) + (todos, id) => todos.find(todo => todo.id === id), ) const selectTodoByIdCurried = createCurriedSelector( [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id) + (todos, id) => todos.find(todo => todo.id === id), ) expect(selectTodoById(state, 0)).toStrictEqual( - selectTodoByIdCurried(0)(state) + selectTodoByIdCurried(0)(state), ) expect(selectTodoById.argsMemoize).toBe(selectTodoByIdCurried.argsMemoize) expect(selectTodoById.lastResult()).toBeDefined() @@ -209,12 +209,12 @@ test('createCurriedSelector copy paste pattern', () => { expect(selectTodoById.lastResult()).toBe(selectTodoByIdCurried.lastResult()) expect(selectTodoById.memoize).toBe(selectTodoByIdCurried.memoize) expect(selectTodoById.memoizedResultFunc(state.todos, 0)).toBe( - selectTodoByIdCurried.memoizedResultFunc(state.todos, 0) + selectTodoByIdCurried.memoizedResultFunc(state.todos, 0), ) expect(selectTodoById.recomputations()).toBe( - selectTodoByIdCurried.recomputations() + selectTodoByIdCurried.recomputations(), ) expect(selectTodoById.resultFunc(state.todos, 0)).toBe( - selectTodoByIdCurried.resultFunc(state.todos, 0) + selectTodoByIdCurried.resultFunc(state.todos, 0), ) }) diff --git a/test/identityFunctionCheck.test.ts b/test/identityFunctionCheck.test.ts index fa7fd225e..0b3a0347c 100644 --- a/test/identityFunctionCheck.test.ts +++ b/test/identityFunctionCheck.test.ts @@ -7,7 +7,7 @@ describe('identityFunctionCheck', () => { const identityFunction = vi.fn((state: T) => state) let badSelector = createSelector( [(state: RootState) => state], - identityFunction + identityFunction, ) afterEach(() => { @@ -15,7 +15,7 @@ describe('identityFunctionCheck', () => { identityFunction.mockClear() badSelector = createSelector( [(state: RootState) => state], - identityFunction + identityFunction, ) }) afterAll(() => { @@ -26,7 +26,7 @@ describe('identityFunctionCheck', () => { ({ state }) => { const goodSelector = createSelector( [(state: RootState) => state], - state => state.todos + state => state.todos, ) expect(goodSelector(state)).toBe(state.todos) @@ -38,7 +38,7 @@ describe('identityFunctionCheck', () => { expect(identityFunction).toHaveBeenCalledTimes(2) expect(consoleSpy).toHaveBeenCalledOnce() - } + }, ) localTest('includes stack with warning', ({ state }) => { @@ -48,11 +48,11 @@ describe('identityFunctionCheck', () => { expect(consoleSpy).toHaveBeenCalledWith( expect.stringContaining( - 'The result function returned its own inputs without modification' + 'The result function returned its own inputs without modification', ), { - stack: expect.any(String) - } + stack: expect.any(String), + }, ) }) @@ -74,7 +74,7 @@ describe('identityFunctionCheck', () => { const badSelector = createSelector( [(state: RootState) => state], identityFunction, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) expect(badSelector(state)).toBe(state) @@ -82,7 +82,7 @@ describe('identityFunctionCheck', () => { expect(identityFunction).toHaveBeenCalledOnce() expect(consoleSpy).not.toHaveBeenCalled() - } + }, ) localTest('disables check in production', ({ state }) => { @@ -102,7 +102,7 @@ describe('identityFunctionCheck', () => { const badSelector = createSelector( [(state: RootState) => state], identityFunction, - { devModeChecks: { identityFunctionCheck: 'once' } } + { devModeChecks: { identityFunctionCheck: 'once' } }, ) expect(badSelector(state)).toBe(state) @@ -121,7 +121,7 @@ describe('identityFunctionCheck', () => { localTest('allows always running the check', () => { const badSelector = createSelector([state => state], identityFunction, { - devModeChecks: { identityFunctionCheck: 'always' } + devModeChecks: { identityFunctionCheck: 'always' }, }) const state = {} @@ -149,7 +149,7 @@ describe('identityFunctionCheck', () => { const badSelector = createSelector( [(state: RootState) => state], identityFunction, - { devModeChecks: {} } + { devModeChecks: {} }, ) expect(badSelector(state)).toBe(state) @@ -169,7 +169,7 @@ describe('identityFunctionCheck', () => { localTest('uses the memoize provided', ({ state }) => { const badSelector = createSelector( [(state: RootState) => state.todos], - identityFunction + identityFunction, ) expect(badSelector(state)).toBe(state.todos) @@ -190,13 +190,13 @@ describe('identityFunctionCheck', () => { // function). const getFirstAlertIfMessageIsEmpty = createSelector( [(state: RootState) => state.alerts[0]], - firstAlert => (!firstAlert.message ? firstAlert : null) + firstAlert => (!firstAlert.message ? firstAlert : null), ) expect(getFirstAlertIfMessageIsEmpty(state)).toBeNull() expect(consoleSpy).not.toHaveBeenCalled() - } + }, ) localTest( @@ -207,13 +207,13 @@ describe('identityFunctionCheck', () => { // result function). const getFirstAlertIfMessageIsNotEmpty = createSelector( [(state: RootState) => state.alerts[0]], - firstAlert => (firstAlert.message ? firstAlert : null) + firstAlert => (firstAlert.message ? firstAlert : null), ) expect(getFirstAlertIfMessageIsNotEmpty(state)).toBe(state.alerts[0]) expect(consoleSpy).not.toHaveBeenCalled() - } + }, ) localTest( @@ -223,14 +223,14 @@ describe('identityFunctionCheck', () => { [ (state: RootState) => state.alerts, (state: RootState) => - state.users.user.details.preferences.notifications.sms + state.users.user.details.preferences.notifications.sms, ], - (alerts, smsEnabled) => (!smsEnabled ? alerts : []) + (alerts, smsEnabled) => (!smsEnabled ? alerts : []), ) expect(getAllNotificationsIfSmsNotEnabled(state)).toBe(state.alerts) expect(consoleSpy).not.toHaveBeenCalled() - } + }, ) }) diff --git a/test/inputStabilityCheck.spec.ts b/test/inputStabilityCheck.spec.ts index 2f7b8b954..4bd82de92 100644 --- a/test/inputStabilityCheck.spec.ts +++ b/test/inputStabilityCheck.spec.ts @@ -3,7 +3,7 @@ import { createSelector, lruMemoize, referenceEqualityCheck, - setGlobalDevModeChecks + setGlobalDevModeChecks, } from 'reselect' import type { RootState } from './testUtils' import { localTest } from './testUtils' @@ -27,7 +27,7 @@ describe('inputStabilityCheck', () => { it('calls each input selector twice, and warns to console if unstable reference is returned', () => { const stableAddNums = createSelector( [(a: number) => a, (a: number, b: number) => b], - (a, b) => a + b + (a, b) => a + b, ) expect(stableAddNums(1, 2)).toBe(3) @@ -44,13 +44,13 @@ describe('inputStabilityCheck', () => { // IArguments isn't an array :( arguments: expect.anything(), firstInputs: expect.arrayContaining([ - expect.objectContaining({ a: 1, b: 2 }) + expect.objectContaining({ a: 1, b: 2 }), ]), secondInputs: expect.arrayContaining([ - expect.objectContaining({ a: 1, b: 2 }) + expect.objectContaining({ a: 1, b: 2 }), ]), - stack: expect.any(String) - }) + stack: expect.any(String), + }), ) }) @@ -68,7 +68,7 @@ describe('inputStabilityCheck', () => { it('disables check if specified in the selector options', () => { const addNums = createSelector([unstableInput], ({ a, b }) => a + b, { - devModeChecks: { inputStabilityCheck: 'never' } + devModeChecks: { inputStabilityCheck: 'never' }, }) expect(addNums(1, 2)).toBe(3) @@ -94,7 +94,7 @@ describe('inputStabilityCheck', () => { it('allows running the check only once', () => { const addNums = createSelector([unstableInput], ({ a, b }) => a + b, { - devModeChecks: { inputStabilityCheck: 'once' } + devModeChecks: { inputStabilityCheck: 'once' }, }) expect(addNums(1, 2)).toBe(3) @@ -112,7 +112,7 @@ describe('inputStabilityCheck', () => { it('allows always running the check', () => { const addNums = createSelector([unstableInput], ({ a, b }) => a + b, { - devModeChecks: { inputStabilityCheck: 'always' } + devModeChecks: { inputStabilityCheck: 'always' }, }) expect(addNums(1, 2)).toBe(3) @@ -136,7 +136,7 @@ describe('inputStabilityCheck', () => { it('runs once when devModeChecks is an empty object', () => { const addNums = createSelector([unstableInput], ({ a, b }) => a + b, { - devModeChecks: {} + devModeChecks: {}, }) expect(addNums(1, 2)).toBe(3) @@ -159,9 +159,9 @@ describe('inputStabilityCheck', () => { { memoize: lruMemoize, memoizeOptions: { - equalityCheck: shallowEqual - } - } + equalityCheck: shallowEqual, + }, + }, ) expect(addNumsShallow(1, 2)).toBe(3) @@ -191,8 +191,8 @@ describe('the effects of inputStabilityCheck with resultEqualityCheck', () => { todos => todos.map(({ id }) => id), { memoizeOptions: { resultEqualityCheck }, - devModeChecks: { inputStabilityCheck: 'once' } - } + devModeChecks: { inputStabilityCheck: 'once' }, + }, ) const firstResult = selectTodoIds(store.getState()) @@ -210,7 +210,7 @@ describe('the effects of inputStabilityCheck with resultEqualityCheck', () => { expect(secondResult).toBe(thirdResult) expect(resultEqualityCheck).not.toHaveBeenCalled() - } + }, ) localTest( @@ -221,8 +221,8 @@ describe('the effects of inputStabilityCheck with resultEqualityCheck', () => { todos => todos.map(({ id }) => id), { memoizeOptions: { resultEqualityCheck }, - devModeChecks: { inputStabilityCheck: 'always' } - } + devModeChecks: { inputStabilityCheck: 'always' }, + }, ) const firstResult = selectTodoIds(store.getState()) @@ -240,7 +240,7 @@ describe('the effects of inputStabilityCheck with resultEqualityCheck', () => { expect(secondResult).toBe(thirdResult) expect(resultEqualityCheck).not.toHaveBeenCalled() - } + }, ) localTest( @@ -251,8 +251,8 @@ describe('the effects of inputStabilityCheck with resultEqualityCheck', () => { todos => todos.map(({ id }) => id), { memoizeOptions: { resultEqualityCheck }, - devModeChecks: { inputStabilityCheck: 'never' } - } + devModeChecks: { inputStabilityCheck: 'never' }, + }, ) const firstResult = selectTodoIds(store.getState()) @@ -270,6 +270,6 @@ describe('the effects of inputStabilityCheck with resultEqualityCheck', () => { expect(secondResult).toBe(thirdResult) expect(resultEqualityCheck).not.toHaveBeenCalled() - } + }, ) }) diff --git a/test/lruMemoize.test.ts b/test/lruMemoize.test.ts index 6c42808eb..bc7b345a0 100644 --- a/test/lruMemoize.test.ts +++ b/test/lruMemoize.test.ts @@ -4,14 +4,14 @@ import { shallowEqual } from 'react-redux' import { createSelectorCreator, lruMemoize, - referenceEqualityCheck + referenceEqualityCheck, } from 'reselect' import type { RootState } from './testUtils' import { localTest, toggleCompleted } from './testUtils' const createSelectorLru = createSelectorCreator({ memoize: lruMemoize, - argsMemoize: lruMemoize + argsMemoize: lruMemoize, }) describe(lruMemoize, () => { @@ -33,7 +33,7 @@ describe(lruMemoize, () => { test('Memoizes with multiple arguments', () => { const memoized = lruMemoize((...args) => - args.reduce((sum, value) => sum + value, 0) + args.reduce((sum, value) => sum + value, 0), ) expect(memoized(1, 2)).toBe(3) expect(memoized(1)).toBe(1) @@ -117,8 +117,8 @@ describe(lruMemoize, () => { return state }, { - maxSize: 3 - } + maxSize: 3, + }, ) // Initial call @@ -167,7 +167,7 @@ describe(lruMemoize, () => { const todos1: Todo[] = [ { id: 1, name: 'a' }, { id: 2, name: 'b' }, - { id: 3, name: 'c' } + { id: 3, name: 'c' }, ] const todos2 = todos1.slice() todos2[2] = { id: 3, name: 'd' } @@ -219,8 +219,8 @@ describe(lruMemoize, () => { }, { maxSize, - resultEqualityCheck: shallowEqual - } + resultEqualityCheck: shallowEqual, + }, ) const ids1 = memoizer(todos1) @@ -244,7 +244,7 @@ describe(lruMemoize, () => { const memoizedFn = lruMemoize(selector, { maxSize: 1, resultEqualityCheck, - equalityCheck + equalityCheck, }) // initialize the cache @@ -269,14 +269,14 @@ describe(lruMemoize, () => { test('Allows caching a value of `undefined`', () => { const state = { foo: { baz: 'baz' }, - bar: 'qux' + bar: 'qux', } const fooChangeSpy = vi.fn() const fooChangeHandler = createSelectorLru( (state: any) => state.foo, - fooChangeSpy + fooChangeSpy, ) fooChangeHandler(state) @@ -308,7 +308,7 @@ describe(lruMemoize, () => { equalityCheck: (a, b) => { memoizer1Calls++ return a === b - } + }, }) acceptsEqualityCheckAsOption(42) @@ -324,7 +324,7 @@ describe(lruMemoize, () => { }, { // no args - } + }, ) const o1 = { a: 1 } @@ -346,8 +346,8 @@ describe(lruMemoize, () => { return state }, { - maxSize: 1 - } + maxSize: 1, + }, ) // Initial call @@ -375,8 +375,8 @@ describe(lruMemoize, () => { }, { memoizeOptions: { maxSize: 3 }, - devModeChecks: { identityFunctionCheck: 'never' } - } + devModeChecks: { identityFunctionCheck: 'never' }, + }, ) // Initial call @@ -428,7 +428,7 @@ describe(lruMemoize, () => { const createSelector = createSelectorCreator({ memoize: lruMemoize, - argsMemoize: lruMemoize + argsMemoize: lruMemoize, }).withTypes() const selector = createSelector( @@ -436,8 +436,8 @@ describe(lruMemoize, () => { state => state, { argsMemoizeOptions: { maxSize: 10 }, - memoizeOptions: { maxSize: 10 } - } + memoizeOptions: { maxSize: 10 }, + }, ) const firstResult = selector(state, 0) @@ -460,15 +460,15 @@ describe(lruMemoize, () => { memoize: lruMemoize, argsMemoize: lruMemoize, memoizeOptions: { maxSize: 0 }, - argsMemoizeOptions: { maxSize: 0 } + argsMemoizeOptions: { maxSize: 0 }, }).withTypes() const selectTodoIds = createSelectorLru([state => state.todos], todos => - todos.map(({ id }) => id) + todos.map(({ id }) => id), ) expect(selectTodoIds(store.getState())).toBe( - selectTodoIds(store.getState()) + selectTodoIds(store.getState()), ) expect(selectTodoIds.recomputations()).toBe(1) @@ -476,18 +476,18 @@ describe(lruMemoize, () => { store.dispatch(toggleCompleted(0)) expect(selectTodoIds(store.getState())).toBe( - selectTodoIds(store.getState()) + selectTodoIds(store.getState()), ) expect(selectTodoIds.recomputations()).toBe(2) const selectTodoIdsLru = lruMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { maxSize: -2 } + { maxSize: -2 }, ) expect(selectTodoIdsLru(state)).toBe(selectTodoIdsLru(state)) - } + }, ) }) @@ -507,7 +507,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { ({ store }) => { const selectTodoIds = lruMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck } + { resultEqualityCheck }, ) const firstResult = selectTodoIds(store.getState()) @@ -519,7 +519,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { expect(firstResult).toBe(secondResult) expect(selectTodoIds.resultsCount()).toBe(1) - } + }, ) localTest( @@ -530,8 +530,8 @@ describe('lruMemoize integration with resultEqualityCheck', () => { todos => todos.map(({ id }) => id), { memoizeOptions: { resultEqualityCheck }, - devModeChecks: { inputStabilityCheck: 'once' } - } + devModeChecks: { inputStabilityCheck: 'once' }, + }, ) expect(selectTodoIds(store.getState())).to.be.an('array').that.is.not @@ -566,7 +566,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { expect(selectTodoIds.resultsCount()).toBe(3) expect(selectTodoIds.dependencyRecomputations()).toBe(3) - } + }, ) localTest( @@ -578,7 +578,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { const selectTodoIdsWithResultEqualityCheck = lruMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck } + { resultEqualityCheck }, ) const firstResultWithResultEqualityCheck = @@ -592,17 +592,17 @@ describe('lruMemoize integration with resultEqualityCheck', () => { selectTodoIdsWithResultEqualityCheck(store.getState()) expect(firstResultWithResultEqualityCheck).not.toBe( - secondResultWithResultEqualityCheck + secondResultWithResultEqualityCheck, ) expect(firstResultWithResultEqualityCheck).toStrictEqual( - secondResultWithResultEqualityCheck + secondResultWithResultEqualityCheck, ) expect(selectTodoIdsWithResultEqualityCheck.resultsCount()).toBe(2) const selectTodoIds = lruMemoize((state: RootState) => - state.todos.map(({ id }) => id) + state.todos.map(({ id }) => id), ) const firstResult = selectTodoIds(store.getState()) @@ -618,7 +618,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { expect(selectTodoIds.resultsCount()).toBe(2) resultEqualityCheck.mockClear() - } + }, ) }) @@ -638,7 +638,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { ({ store }) => { const selectTodoIds = lruMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck } + { resultEqualityCheck }, ) const firstResult = selectTodoIds(store.getState()) @@ -650,7 +650,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { expect(firstResult).toBe(secondResult) expect(selectTodoIds.resultsCount()).toBe(1) - } + }, ) localTest( @@ -661,8 +661,8 @@ describe('lruMemoize integration with resultEqualityCheck', () => { todos => todos.map(({ id }) => id), { memoizeOptions: { resultEqualityCheck }, - devModeChecks: { inputStabilityCheck: 'once' } - } + devModeChecks: { inputStabilityCheck: 'once' }, + }, ) expect(selectTodoIds(store.getState())).to.be.an('array').that.is.not @@ -697,7 +697,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { expect(selectTodoIds.resultsCount()).toBe(3) expect(selectTodoIds.dependencyRecomputations()).toBe(3) - } + }, ) localTest( @@ -709,7 +709,7 @@ describe('lruMemoize integration with resultEqualityCheck', () => { const selectTodoIdsWithResultEqualityCheck = lruMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck } + { resultEqualityCheck }, ) const firstResultWithResultEqualityCheck = @@ -723,17 +723,17 @@ describe('lruMemoize integration with resultEqualityCheck', () => { selectTodoIdsWithResultEqualityCheck(store.getState()) expect(firstResultWithResultEqualityCheck).not.toBe( - secondResultWithResultEqualityCheck + secondResultWithResultEqualityCheck, ) expect(firstResultWithResultEqualityCheck).toStrictEqual( - secondResultWithResultEqualityCheck + secondResultWithResultEqualityCheck, ) expect(selectTodoIdsWithResultEqualityCheck.resultsCount()).toBe(2) const selectTodoIds = lruMemoize((state: RootState) => - state.todos.map(({ id }) => id) + state.todos.map(({ id }) => id), ) const firstResult = selectTodoIds(store.getState()) @@ -749,6 +749,6 @@ describe('lruMemoize integration with resultEqualityCheck', () => { expect(selectTodoIds.resultsCount()).toBe(2) resultEqualityCheck.mockClear() - } + }, ) }) diff --git a/test/perfComparisons.spec.ts b/test/perfComparisons.spec.ts index 506703977..df3b99dc6 100644 --- a/test/perfComparisons.spec.ts +++ b/test/perfComparisons.spec.ts @@ -4,7 +4,7 @@ import { unstable_autotrackMemoize as autotrackMemoize, createSelectorCreator, lruMemoize, - weakMapMemoize + weakMapMemoize, } from 'reselect' import { vi } from 'vitest' @@ -37,14 +37,14 @@ describe('More perf comparisons', () => { really: { deeply: { nested: { - c1: { value: 0 } - } - } - } - } + c1: { value: 0 }, + }, + }, + }, + }, }, - c2: { value: 0 } + c2: { value: 0 }, }, reducers: { increment1(state) { @@ -53,8 +53,8 @@ describe('More perf comparisons', () => { }, increment2(state) { state.c2.value++ - } - } + }, + }, }) const todosSlice = createSlice({ @@ -62,7 +62,7 @@ describe('More perf comparisons', () => { initialState: [ { id: 0, name: 'a', completed: false }, { id: 1, name: 'b', completed: false }, - { id: 2, name: 'c', completed: false } + { id: 2, name: 'c', completed: false }, ] as TodosState, reducers: { toggleCompleted(state, action: PayloadAction) { @@ -73,20 +73,20 @@ describe('More perf comparisons', () => { }, setName(state) { state[1].name = 'd' - } - } + }, + }, }) const store = configureStore({ reducer: { counter: counterSlice.reducer, - todos: todosSlice.reducer + todos: todosSlice.reducer, }, middleware: gDM => gDM({ serializableCheck: false, - immutableCheck: false - }) + immutableCheck: false, + }), }) type RootState = ReturnType @@ -112,7 +112,7 @@ describe('More perf comparisons', () => { (state: RootState) => state.counter.c2.value, (c1, c2) => { return c1 + c2 - } + }, ) const cdCounters2 = csDefault( @@ -120,21 +120,21 @@ describe('More perf comparisons', () => { (state: RootState) => state.counter.c2, (c1, c2) => { return c1.value + c2.value - } + }, ) const cdTodoIds = csDefault( (state: RootState) => state.todos, todos => { return todos.map(todo => todo.id) - } + }, ) const cdTodoIdsAndNames = csDefault( (state: RootState) => state.todos, todos => { return todos.map(todo => ({ id: todo.id, name: todo.name })) - } + }, ) const cdCompletedTodos = csDefault( @@ -142,7 +142,7 @@ describe('More perf comparisons', () => { todos => { const completed = todos.filter(todo => todo.completed) return completed.length - } + }, ) const cdCompletedTodos2 = csDefault( @@ -150,7 +150,7 @@ describe('More perf comparisons', () => { todos => { const completed = todos.filter(todo => todo.completed) return completed.length - } + }, ) const caCounters1 = csDefault( @@ -159,7 +159,7 @@ describe('More perf comparisons', () => { (state: RootState) => state.counter.c2.value, (c1, c2) => { return c1 + c2 - } + }, ) const caCounters2 = csAutotrack( @@ -168,21 +168,21 @@ describe('More perf comparisons', () => { (c1, c2) => { // console.log('inside caCounters2: ', { c1, c2 }) return c1.value + c2.value - } + }, ) const caTodoIds = csAutotrack( (state: RootState) => state.todos, todos => { return todos.map(todo => todo.id) - } + }, ) const caTodoIdsAndNames = csAutotrack( (state: RootState) => state.todos, todos => { return todos.map(todo => ({ id: todo.id, name: todo.name })) - } + }, ) const caCompletedTodos = csAutotrack( @@ -190,7 +190,7 @@ describe('More perf comparisons', () => { todos => { const completed = todos.filter(todo => todo.completed) return completed.length - } + }, ) const caCompletedTodos2 = csAutotrack( @@ -198,7 +198,7 @@ describe('More perf comparisons', () => { todos => { const completed = todos.filter(todo => todo.completed) return completed.length - } + }, ) const defaultStart = performance.now() @@ -238,7 +238,7 @@ describe('More perf comparisons', () => { caTodoIds, caTodoIdsAndNames, caCompletedTodos, - caCompletedTodos2 + caCompletedTodos2, } // console.log('\nTotal recomputations:') @@ -266,7 +266,7 @@ describe('More perf comparisons', () => { (c1, c2) => { // console.log('inside caCounters2: ', { c1, c2 }) return c1.value + c2.value - } + }, ) for (let i = 0; i < 10; i++) { @@ -293,13 +293,13 @@ describe('More perf comparisons', () => { const store = configureStore({ reducer: { counter: counterSlice.reducer, - todos: todosSlice.reducer + todos: todosSlice.reducer, }, middleware: gDM => gDM({ serializableCheck: false, - immutableCheck: false - }) + immutableCheck: false, + }), }) const reduxStates: RootState[] = [] @@ -318,7 +318,7 @@ describe('More perf comparisons', () => { todos => { // console.log('Recalculating todo IDs') return todos.map(todo => ({ id: todo.id, name: todo.name })) - } + }, ) for (const state of reduxStates) { diff --git a/test/reselect.spec.ts b/test/reselect.spec.ts index a11767a41..990629240 100644 --- a/test/reselect.spec.ts +++ b/test/reselect.spec.ts @@ -7,7 +7,7 @@ import { createSelector, createSelectorCreator, lruMemoize, - weakMapMemoize + weakMapMemoize, } from 'reselect' import type { OutputSelector, OutputSelectorFields } from 'reselect' @@ -18,7 +18,7 @@ import { isMemoizedSelector, localTest, setEnvToProd, - toggleCompleted + toggleCompleted, } from './testUtils' // Construct 1E6 states for perf test outside of the perf test so as to not change the execute time of the test function @@ -49,7 +49,7 @@ describe('Basic selector behavior', () => { const selector = createSelector( (state: StateA) => state.a, a => a, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const firstState = { a: 1 } const firstStateNewPointer = { a: 1 } @@ -68,7 +68,7 @@ describe('Basic selector behavior', () => { const selector = createSelector( (...params: any[]) => params.length, a => a, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) expect(selector({})).toBe(1) }) @@ -77,7 +77,7 @@ describe('Basic selector behavior', () => { const selector = createSelector( (state: StateAB) => state.a, (state: StateAB) => state.b, - (a, b) => a + b + (a, b) => a + b, ) const state1 = { a: 1, b: 2 } expect(selector(state1)).toBe(3) @@ -98,17 +98,17 @@ describe('Basic selector behavior', () => { return state.b }, 'not a function', - (a: any, b: any) => a + b - ) + (a: any, b: any) => a + b, + ), ).toThrow( - 'createSelector expects all input-selectors to be functions, but received the following types: [function unnamed(), function input2(), string]' + 'createSelector expects all input-selectors to be functions, but received the following types: [function unnamed(), function input2(), string]', ) expect(() => // @ts-ignore - createSelector((state: StateAB) => state.a, 'not a function') + createSelector((state: StateAB) => state.a, 'not a function'), ).toThrow( - 'createSelector expects an output function after the inputs, but received: [string]' + 'createSelector expects an output function after the inputs, but received: [string]', ) }) @@ -122,7 +122,7 @@ describe('Basic selector behavior', () => { const selector = createSelector( (state: StateAB) => state.a, (state: StateAB) => state.b, - (a, b) => a + b + (a, b) => a + b, ) const state1 = { a: 1, b: 2 } @@ -145,7 +145,7 @@ describe('Basic selector behavior', () => { const selector = createSelector( (state: StateAB) => state.a, (state: StateAB) => state.b, - (a, b) => a + b + (a, b) => a + b, ) const start = new Date() @@ -159,14 +159,14 @@ describe('Basic selector behavior', () => { // Expected a million calls to a selector with the same arguments to take less than 1 second expect(totalTime).toBeLessThan(2000) - } + }, ) }) test('memoized composite arguments', () => { const selector = createSelector( (state: StateSub) => state.sub, sub => sub, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const state1 = { sub: { a: 1 } } expect(selector(state1)).toEqual({ a: 1 }) @@ -182,7 +182,7 @@ describe('Basic selector behavior', () => { [state => state.a, state => state.b], (a, b) => { return a + b - } + }, ) expect(selector({ a: 1, b: 2 })).toBe(3) expect(selector({ a: 1, b: 2 })).toBe(3) @@ -200,7 +200,7 @@ describe('Basic selector behavior', () => { (a, b, c) => { called++ return a + b + c - } + }, ) expect(selector({ a: 1, b: 2 }, { c: 100 })).toBe(103) }) @@ -212,7 +212,7 @@ describe('Basic selector behavior', () => { () => { called++ throw Error('test error') - } + }, ) expect(() => selector({ a: 1 })).toThrow('test error') expect(() => selector({ a: 1 })).toThrow('test error') @@ -228,7 +228,7 @@ describe('Basic selector behavior', () => { if (a > 1) throw Error('test error') return a }, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const state1 = { a: 1 } const state2 = { a: 2 } @@ -244,7 +244,7 @@ describe('Combining selectors', () => { const selector1 = createSelector( (state: StateSub) => state.sub, sub => sub, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const selector2 = createSelector(selector1, sub => sub.a) const state1 = { sub: { a: 1 } } @@ -260,12 +260,12 @@ describe('Combining selectors', () => { const selector1 = createSelector( (state: StateSub) => state.sub, (state: StateSub, props: { x: number; y: number }) => props.x, - (sub, x) => ({ sub, x }) + (sub, x) => ({ sub, x }), ) const selector2 = createSelector( selector1, (state: StateSub, props: { x: number; y: number }) => props.y, - (param, y) => param.sub.a + param.x + y + (param, y) => param.sub.a + param.x + y, ) const state1 = { sub: { a: 1 } } expect(selector2(state1, { x: 100, y: 200 })).toBe(301) @@ -281,12 +281,12 @@ describe('Combining selectors', () => { (state: StateSub) => state.sub, (state: StateSub, props: { x: number; y: number }, another: number) => props.x + another, - (sub, x) => ({ sub, x }) + (sub, x) => ({ sub, x }), ) const selector2 = createSelector( selector1, (state: StateSub, props: { x: number; y: number }) => props.y, - (param, y) => param.sub.a + param.x + y + (param, y) => param.sub.a + param.x + y, ) const state1 = { sub: { a: 1 } } expect(selector2(state1, { x: 100, y: 200 }, 100)).toBe(401) @@ -301,12 +301,12 @@ describe('Combining selectors', () => { // a rather absurd equals operation we can verify in tests const createOverridenSelector = createSelectorCreator( lruMemoize, - (a, b) => typeof a === typeof b + (a, b) => typeof a === typeof b, ) const selector = createOverridenSelector( (state: StateA) => state.a, a => a, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) expect(selector({ a: 1 })).toBe(1) expect(selector({ a: 2 })).toBe(1) // yes, really true @@ -325,7 +325,7 @@ describe('Customizing selectors', () => { const selector = customSelectorCreator( (state: StateAB) => state.a, (state: StateAB) => state.b, - (a, b) => a + b + (a, b) => a + b, ) expect(selector({ a: 1, b: 2 })).toBe(3) expect(selector({ a: 1, b: 2 })).toBe(3) @@ -353,8 +353,8 @@ describe('Customizing selectors', () => { memoizeOptions: (a, b) => { memoizer1Calls++ return a === b - } - } + }, + }, ) lruMemoizeAcceptsFirstArgDirectly({ a: 1, b: 2 }) @@ -372,9 +372,9 @@ describe('Customizing selectors', () => { (a, b) => { memoizer2Calls++ return a === b - } - ] - } + }, + ], + }, ) lruMemoizeAcceptsArgsAsArray({ a: 1, b: 2 }) @@ -387,13 +387,13 @@ describe('Customizing selectors', () => { (a, b) => { memoizer3Calls++ return a === b - } + }, ) const lruMemoizeAcceptsArgFromCSC = createSelectorWithSeparateArg( (state: StateAB) => state.a, (state: StateAB) => state.b, - (a, b) => a + b + (a, b) => a + b, ) lruMemoizeAcceptsArgFromCSC({ a: 1, b: 2 }) @@ -414,22 +414,22 @@ describe('Customizing selectors', () => { devModeChecks: { inputStabilityCheck: 'always' }, memoizeOptions: { equalityCheck: (a, b) => false, - resultEqualityCheck: (a, b) => false - } - } + resultEqualityCheck: (a, b) => false, + }, + }, ) selectorOriginal(deepClone(state)) selectorOriginal(deepClone(state)) const selectorDefaultParametric = createSelector( [ (state: RootState, id: number) => id, - (state: RootState) => state.todos + (state: RootState) => state.todos, ], - (id, todos) => todos.filter(todo => todo.id === id) + (id, todos) => todos.filter(todo => todo.id === id), ) selectorDefaultParametric(state, 1) selectorDefaultParametric(state, 1) - } + }, ) }) @@ -438,12 +438,12 @@ describe('argsMemoize and memoize', () => { const state = store.getState() const selectorDefault = createSelector( (state: RootState) => state.todos, - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const selectorDefaultParametric = createSelector( [(state: RootState, id: number) => id, (state: RootState) => state.todos], (id, todos) => todos.filter(todo => todo.id === id), - { memoize: lruMemoize } + { memoize: lruMemoize }, ) selectorDefaultParametric(state, 0) selectorDefaultParametric(state, 1) @@ -455,7 +455,7 @@ describe('argsMemoize and memoize', () => { const selectorAutotrack = createSelector( (state: RootState) => state.todos, todos => todos.map(({ id }) => id), - { memoize: autotrackMemoize } + { memoize: autotrackMemoize }, ) const outPutSelectorFields: (keyof OutputSelectorFields)[] = [ 'memoize', @@ -467,7 +467,7 @@ describe('argsMemoize and memoize', () => { 'recomputations', 'resetRecomputations', 'dependencyRecomputations', - 'resetDependencyRecomputations' + 'resetDependencyRecomputations', ] const memoizerFields: Exclude< keyof OutputSelector, @@ -475,7 +475,7 @@ describe('argsMemoize and memoize', () => { >[] = ['clearCache', 'resultsCount', 'resetResultsCount'] const allFields: (keyof OutputSelector)[] = [ ...outPutSelectorFields, - ...memoizerFields + ...memoizerFields, ] const hasUndefinedValues = (object: object) => { return Object.values(object).some(e => e == null) @@ -541,8 +541,8 @@ describe('argsMemoize and memoize', () => { store.dispatch( addTodo({ title: 'Figure out if plants are really plotting world domination.', - description: 'They may be.' - }) + description: 'They may be.', + }), ) selectorAutotrack(store.getState()) expect(selectorAutotrack.recomputations()).toBe(2) @@ -552,20 +552,20 @@ describe('argsMemoize and memoize', () => { localTest('passing argsMemoize directly to createSelector', ({ store }) => { const otherCreateSelector = createSelectorCreator({ memoize: microMemoize, - argsMemoize: microMemoize + argsMemoize: microMemoize, }) const selectorDefault = otherCreateSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: lruMemoize, argsMemoize: lruMemoize } + { memoize: lruMemoize, argsMemoize: lruMemoize }, ) const selectorAutotrack = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: autotrackMemoize } + { memoize: autotrackMemoize }, ) expect(selectorDefault(store.getState())).toStrictEqual( - selectorAutotrack(store.getState()) + selectorAutotrack(store.getState()), ) expect(selectorDefault.recomputations()).toBe(1) expect(selectorAutotrack.recomputations()).toBe(1) @@ -627,7 +627,7 @@ describe('argsMemoize and memoize', () => { // original options untouched. const selectorOriginal = createSelector( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) selectorOriginal(store.getState()) const start = performance.now() @@ -645,8 +645,8 @@ describe('argsMemoize and memoize', () => { memoize: lruMemoize, // WARNING!! This is just for testing purposes, do not use `autotrackMemoize` to memoize the arguments, // it can return false positives, since it's not tracking a nested field. - argsMemoize: autotrackMemoize - } + argsMemoize: autotrackMemoize, + }, ) selectorOverrideArgsMemoize(store.getState()) for (let i = 0; i < 10; i++) { @@ -660,7 +660,7 @@ describe('argsMemoize and memoize', () => { expect(selectorOriginal.dependencyRecomputations()).toBe(11) const selectorDefaultParametric = createSelector( [(state: RootState, id: number) => id, (state: RootState) => state.todos], - (id, todos) => todos.filter(todo => todo.id === id) + (id, todos) => todos.filter(todo => todo.id === id), ) selectorDefaultParametric(store.getState(), 1) selectorDefaultParametric(store.getState(), 1) @@ -676,24 +676,24 @@ describe('argsMemoize and memoize', () => { const selectorDefaultParametricArgsWeakMap = createSelector( [(state: RootState, id: number) => id, (state: RootState) => state.todos], (id, todos) => todos.filter(todo => todo.id === id), - { argsMemoize: weakMapMemoize } + { argsMemoize: weakMapMemoize }, ) const selectorDefaultParametricWeakMap = createSelector( [(state: RootState, id: number) => id, (state: RootState) => state.todos], (id, todos) => todos.filter(todo => todo.id === id), - { memoize: weakMapMemoize } + { memoize: weakMapMemoize }, ) selectorDefaultParametricArgsWeakMap(store.getState(), 1) selectorDefaultParametricArgsWeakMap(store.getState(), 1) expect(selectorDefaultParametricArgsWeakMap.recomputations()).toBe(1) expect( - selectorDefaultParametricArgsWeakMap.dependencyRecomputations() + selectorDefaultParametricArgsWeakMap.dependencyRecomputations(), ).toBe(1) selectorDefaultParametricArgsWeakMap(store.getState(), 2) selectorDefaultParametricArgsWeakMap(store.getState(), 1) expect(selectorDefaultParametricArgsWeakMap.recomputations()).toBe(2) expect( - selectorDefaultParametricArgsWeakMap.dependencyRecomputations() + selectorDefaultParametricArgsWeakMap.dependencyRecomputations(), ).toBe(2) selectorDefaultParametricArgsWeakMap(store.getState(), 2) // If we call the selector with 1, then 2, then 1 and back to 2 again, @@ -701,7 +701,7 @@ describe('argsMemoize and memoize', () => { // but weakMapMemoize will recompute only twice. expect(selectorDefaultParametricArgsWeakMap.recomputations()).toBe(2) expect( - selectorDefaultParametricArgsWeakMap.dependencyRecomputations() + selectorDefaultParametricArgsWeakMap.dependencyRecomputations(), ).toBe(2) for (let i = 0; i < 10; i++) { selectorDefaultParametricArgsWeakMap(store.getState(), 1) @@ -712,7 +712,7 @@ describe('argsMemoize and memoize', () => { } expect(selectorDefaultParametricArgsWeakMap.recomputations()).toBe(5) expect( - selectorDefaultParametricArgsWeakMap.dependencyRecomputations() + selectorDefaultParametricArgsWeakMap.dependencyRecomputations(), ).toBe(5) for (let i = 0; i < 10; i++) { selectorDefaultParametric(store.getState(), 1) @@ -740,11 +740,11 @@ describe('argsMemoize and memoize', () => { memoize: microMemoize, memoizeOptions: { isEqual: (a, b) => a === b }, argsMemoize: microMemoize, - argsMemoizeOptions: { isEqual: (a, b) => a === b } + argsMemoizeOptions: { isEqual: (a, b) => a === b }, }) const selectorMicroMemoize = createSelectorMicroMemoize( (state: RootState) => state.todos, - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) expect(selectorMicroMemoize(state)).to.be.an('array').that.is.not.empty // Checking existence of fields related to `argsMemoize` @@ -773,9 +773,9 @@ describe('argsMemoize and memoize', () => { id: 0, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } - ]) + description: 'Just do it', + }, + ]), ).to.be.an('array').that.is.not.empty expect(selectorMicroMemoize.recomputations()).to.be.a('number') expect(selectorMicroMemoize.dependencyRecomputations()).to.be.a('number') @@ -786,15 +786,15 @@ describe('argsMemoize and memoize', () => { id: 0, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } - ]) + description: 'Just do it', + }, + ]), ).to.be.an('array').that.is.not.empty const selectorMicroMemoizeOverridden = createSelectorMicroMemoize( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: lruMemoize, argsMemoize: lruMemoize } + { memoize: lruMemoize, argsMemoize: lruMemoize }, ) expect(selectorMicroMemoizeOverridden(state)).to.be.an('array').that.is.not .empty @@ -810,21 +810,21 @@ describe('argsMemoize and memoize', () => { expect(selectorMicroMemoizeOverridden.options).toBeUndefined() // Checking existence of fields related to `memoize` expect( - selectorMicroMemoizeOverridden.memoizedResultFunc.clearCache + selectorMicroMemoizeOverridden.memoizedResultFunc.clearCache, ).to.be.a('function') expect( // @ts-expect-error - selectorMicroMemoizeOverridden.memoizedResultFunc.cache + selectorMicroMemoizeOverridden.memoizedResultFunc.cache, ).toBeUndefined() // @ts-expect-error expect(selectorMicroMemoizeOverridden.memoizedResultFunc.fn).toBeUndefined() expect( // @ts-expect-error - selectorMicroMemoizeOverridden.memoizedResultFunc.isMemoized + selectorMicroMemoizeOverridden.memoizedResultFunc.isMemoized, ).toBeUndefined() expect( // @ts-expect-error - selectorMicroMemoizeOverridden.memoizedResultFunc.options + selectorMicroMemoizeOverridden.memoizedResultFunc.options, ).toBeUndefined() // Checking existence of fields related to the actual memoized selector expect(selectorMicroMemoizeOverridden.dependencies).to.be.an('array').that @@ -837,13 +837,13 @@ describe('argsMemoize and memoize', () => { id: 0, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } - ]) + description: 'Just do it', + }, + ]), ).to.be.an('array').that.is.not.empty expect(selectorMicroMemoizeOverridden.recomputations()).to.be.a('number') expect(selectorMicroMemoizeOverridden.dependencyRecomputations()).to.be.a( - 'number' + 'number', ) expect( selectorMicroMemoizeOverridden.resultFunc([ @@ -851,9 +851,9 @@ describe('argsMemoize and memoize', () => { id: 0, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } - ]) + description: 'Just do it', + }, + ]), ).to.be.an('array').that.is.not.empty const selectorMicroMemoizeOverrideArgsMemoizeOnly = @@ -862,14 +862,14 @@ describe('argsMemoize and memoize', () => { todos => todos.map(({ id }) => id), { argsMemoize: lruMemoize, - argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b } - } + argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }, ) expect(selectorMicroMemoizeOverrideArgsMemoizeOnly(state)).to.be.an('array') .that.is.not.empty // Checking existence of fields related to `argsMemoize` expect(selectorMicroMemoizeOverrideArgsMemoizeOnly.clearCache).to.be.a( - 'function' + 'function', ) // @ts-expect-error expect(selectorMicroMemoizeOverrideArgsMemoizeOnly.cache).toBeUndefined() @@ -877,7 +877,7 @@ describe('argsMemoize and memoize', () => { expect(selectorMicroMemoizeOverrideArgsMemoizeOnly.fn).toBeUndefined() expect( // @ts-expect-error - selectorMicroMemoizeOverrideArgsMemoizeOnly.isMemoized + selectorMicroMemoizeOverrideArgsMemoizeOnly.isMemoized, ).toBeUndefined() // @ts-expect-error expect(selectorMicroMemoizeOverrideArgsMemoizeOnly.options).toBeUndefined() @@ -885,26 +885,26 @@ describe('argsMemoize and memoize', () => { // Checking existence of fields related to `memoize` // @ts-expect-error Note that since we did not override `memoize` in the options object, // memoizedResultFunc.clearCache becomes an invalid field access, and we get `cache`, `fn`, `isMemoized` and `options` instead. - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.clearCache + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.clearCache, ).toBeUndefined() expect( - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.cache + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.cache, ).to.be.a('object') expect( - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.fn + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.fn, ).to.be.a('function') expect( - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.isMemoized + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.isMemoized, ).to.be.true expect( - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.options + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.options, ).to.be.a('object') // Checking existence of fields related to the actual memoized selector expect(selectorMicroMemoizeOverrideArgsMemoizeOnly.dependencies).to.be.an( - 'array' + 'array', ).that.is.not.empty expect(selectorMicroMemoizeOverrideArgsMemoizeOnly.lastResult()).to.be.an( - 'array' + 'array', ).that.is.not.empty expect( selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc([ @@ -912,15 +912,15 @@ describe('argsMemoize and memoize', () => { id: 0, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } - ]) + description: 'Just do it', + }, + ]), ).to.be.an('array').that.is.not.empty expect( - selectorMicroMemoizeOverrideArgsMemoizeOnly.recomputations() + selectorMicroMemoizeOverrideArgsMemoizeOnly.recomputations(), ).to.be.a('number') expect( - selectorMicroMemoizeOverrideArgsMemoizeOnly.dependencyRecomputations() + selectorMicroMemoizeOverrideArgsMemoizeOnly.dependencyRecomputations(), ).to.be.a('number') expect( selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc([ @@ -928,15 +928,15 @@ describe('argsMemoize and memoize', () => { id: 0, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } - ]) + description: 'Just do it', + }, + ]), ).to.be.an('array').that.is.not.empty const selectorMicroMemoizeOverrideMemoizeOnly = createSelectorMicroMemoize( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: lruMemoize } + { memoize: lruMemoize }, ) expect(selectorMicroMemoizeOverrideMemoizeOnly(state)).to.be.an('array') .that.is.not.empty @@ -958,7 +958,7 @@ describe('argsMemoize and memoize', () => { 'memoize', 'argsMemoize', 'dependencyRecomputations', - 'resetDependencyRecomputations' + 'resetDependencyRecomputations', ]) expect(selectorMicroMemoizeOverrideMemoizeOnly.cache).to.be.an('object') expect(selectorMicroMemoizeOverrideMemoizeOnly.fn).to.be.a('function') @@ -969,14 +969,14 @@ describe('argsMemoize and memoize', () => { .to.be.a('function') .that.has.all.keys(['clearCache', 'resultsCount', 'resetResultsCount']) expect( - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.clearCache + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.clearCache, ).to.be.a('function') // Checking existence of fields related to the actual memoized selector expect(selectorMicroMemoizeOverrideMemoizeOnly.dependencies).to.be.an( - 'array' + 'array', ).that.is.not.empty expect(selectorMicroMemoizeOverrideMemoizeOnly.lastResult()).to.be.an( - 'array' + 'array', ).that.is.not.empty expect( selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc([ @@ -984,15 +984,15 @@ describe('argsMemoize and memoize', () => { id: 0, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } - ]) + description: 'Just do it', + }, + ]), ).to.be.an('array').that.is.not.empty expect(selectorMicroMemoizeOverrideMemoizeOnly.recomputations()).to.be.a( - 'number' + 'number', ) expect( - selectorMicroMemoizeOverrideMemoizeOnly.dependencyRecomputations() + selectorMicroMemoizeOverrideMemoizeOnly.dependencyRecomputations(), ).to.be.a('number') expect( selectorMicroMemoizeOverrideMemoizeOnly.resultFunc([ @@ -1000,9 +1000,9 @@ describe('argsMemoize and memoize', () => { id: 0, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } - ]) + description: 'Just do it', + }, + ]), ).to.be.an('array').that.is.not.empty }) @@ -1011,36 +1011,36 @@ describe('argsMemoize and memoize', () => { ({ store, state }) => { const createSelectorMicro = createSelectorCreator({ memoize: microMemoize, - memoizeOptions: { isEqual: (a, b) => a === b } + memoizeOptions: { isEqual: (a, b) => a === b }, }) const selectorMicro = createSelectorMicro( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const selector = createSelector( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const selector1 = createSelector( [(state: RootState) => state.todos], todos => todos.map(({ id }) => id), - { memoize: weakMapMemoize } + { memoize: weakMapMemoize }, ) expect(() => //@ts-expect-error - createSelectorMicro([(state: RootState) => state.todos], 'a') + createSelectorMicro([(state: RootState) => state.todos], 'a'), ).toThrowError( TypeError( - `createSelector expects an output function after the inputs, but received: [string]` - ) + `createSelector expects an output function after the inputs, but received: [string]`, + ), ) const selectorDefault = createSelector( (state: RootState) => state.users, - users => users.user.details.preferences.notifications.push.frequency + users => users.user.details.preferences.notifications.push.frequency, ) const selectorDefault1 = createSelector( (state: RootState) => state.users.user, - user => user.details.preferences.notifications.push.frequency + user => user.details.preferences.notifications.push.frequency, ) let called = 0 const selectorDefault2 = createSelector( @@ -1074,7 +1074,7 @@ describe('argsMemoize and memoize', () => { users => { return users.user.details.preferences.notifications.push.frequency }, - { devModeChecks: { inputStabilityCheck: 'never' } } + { devModeChecks: { inputStabilityCheck: 'never' } }, ) const start = performance.now() for (let i = 0; i < 10_000_000; i++) { @@ -1092,6 +1092,6 @@ describe('argsMemoize and memoize', () => { expect(stateBeforeChange1.todos[1]).toBe(stateAfterChange.todos[1]) expect(stateBeforeChange1).toBe(stateBeforeChange) expect(stateBeforeChange1.alerts).toBe(stateBeforeChange.alerts) - } + }, ) }) diff --git a/test/selectorUtils.spec.ts b/test/selectorUtils.spec.ts index 47b3508dd..60fcc4103 100644 --- a/test/selectorUtils.spec.ts +++ b/test/selectorUtils.spec.ts @@ -9,8 +9,8 @@ describe('createSelector exposed utils', () => { { memoize: lruMemoize, argsMemoize: lruMemoize, - devModeChecks: { identityFunctionCheck: 'never' } - } + devModeChecks: { identityFunctionCheck: 'never' }, + }, ) expect(selector({ a: 1 })).toBe(1) expect(selector({ a: 1 })).toBe(1) @@ -50,7 +50,7 @@ describe('createSelector exposed utils', () => { const selector = createSelector( (state: StateAB) => state.a, (state: StateAB) => state.b, - (a, b) => a + b + (a, b) => a + b, ) const result = selector({ a: 1, b: 2 }) diff --git a/test/setup.vitest.ts b/test/setup.vitest.ts index b4e4f2746..da03fba4d 100644 --- a/test/setup.vitest.ts +++ b/test/setup.vitest.ts @@ -6,7 +6,7 @@ expect.extend({ return { pass: isMemoizedSelector(received), - message: () => `${received} is${isNot ? '' : ' not'} a memoized selector` + message: () => `${received} is${isNot ? '' : ' not'} a memoized selector`, } - } + }, }) diff --git a/test/testUtils.ts b/test/testUtils.ts index 9c3c0bf0d..781ec62c7 100644 --- a/test/testUtils.ts +++ b/test/testUtils.ts @@ -1,546 +1,546 @@ -import type { PayloadAction } from '@reduxjs/toolkit' -import { combineReducers, configureStore, createSlice } from '@reduxjs/toolkit' -import { test } from 'vitest' -import type { AnyFunction, OutputSelector, Simplify } from '../src/types' - -export interface Todo { - id: number - title: string - description: string - completed: boolean -} - -interface Alert { - id: number - message: string - type: string - read: boolean -} - -interface BillingAddress { - street: string - city: string - state: string - zip: string -} - -interface Address extends BillingAddress { - billing: BillingAddress -} - -interface PushNotification { - enabled: boolean - frequency: string -} - -interface Notifications { - email: boolean - sms: boolean - push: PushNotification -} - -interface Preferences { - newsletter: boolean - notifications: Notifications -} - -interface Login { - lastLogin: string - loginCount: number -} - -interface UserDetails { - name: string - email: string - address: Address - preferences: Preferences -} - -interface User { - id: number - details: UserDetails - status: string - login: Login -} - -interface AppSettings { - theme: string - language: string -} - -interface UserState { - user: User - appSettings: AppSettings -} - -let nextTodoId = 0 - -// For long arrays -const todoState = [ - { - id: nextTodoId++, - title: 'Buy groceries', - description: 'Milk, bread, eggs, and fruits', - completed: false - }, - { - id: nextTodoId++, - title: 'Schedule dentist appointment', - description: 'Check available slots for next week', - completed: false - }, - { - id: nextTodoId++, - title: 'Convince the cat to get a job', - description: 'Need extra income for cat treats', - completed: false - }, - { - id: nextTodoId++, - title: 'Figure out if plants are plotting world domination', - description: 'That cactus looks suspicious...', - completed: false - }, - { - id: nextTodoId++, - title: 'Practice telekinesis', - description: 'Try moving the remote without getting up', - completed: false - }, - { - id: nextTodoId++, - title: 'Determine location of El Dorado', - description: 'Might need it for the next vacation', - completed: false - }, - { - id: nextTodoId++, - title: 'Master the art of invisible potato juggling', - description: 'Great party trick', - completed: false - } -] - -export const createTodoItem = () => { - const id = nextTodoId++ - return { - id, - title: `Task ${id}`, - description: `Description for task ${id}`, - completed: false - } -} - -export const pushToTodos = (limit: number) => { - const { length: todoStateLength } = todoState - // const limit = howMany + todoStateLength - for (let i = todoStateLength; i < limit; i++) { - todoState.push(createTodoItem()) - } -} - -pushToTodos(200) - -const alertState = [ - { - id: 0, - message: 'You have an upcoming meeting at 3 PM.', - type: 'reminder', - read: false - }, - { - id: 1, - message: 'New software update available.', - type: 'notification', - read: false - }, - { - id: 3, - message: - 'The plants have been watered, but keep an eye on that shifty cactus.', - type: 'notification', - read: false - }, - { - id: 4, - message: - 'Telekinesis class has been moved to 5 PM. Please do not bring any spoons.', - type: 'reminder', - read: false - }, - { - id: 5, - message: - 'Expedition to El Dorado is postponed. The treasure map is being updated.', - type: 'notification', - read: false - }, - { - id: 6, - message: - 'Invisible potato juggling championship is tonight. May the best mime win.', - type: 'reminder', - read: false - } -] - -// For nested fields tests -const userState: UserState = { - user: { - id: 0, - details: { - name: 'John Doe', - email: 'john.doe@example.com', - address: { - street: '123 Main St', - city: 'AnyTown', - state: 'CA', - zip: '12345', - billing: { - street: '456 Main St', - city: 'AnyTown', - state: 'CA', - zip: '12345' - } - }, - preferences: { - newsletter: true, - notifications: { - email: true, - sms: false, - push: { - enabled: true, - frequency: 'daily' - } - } - } - }, - status: 'active', - login: { - lastLogin: '2023-04-30T12:34:56Z', - loginCount: 123 - } - }, - appSettings: { - theme: 'dark', - language: 'en-US' - } -} - -const todoSlice = createSlice({ - name: 'todos', - initialState: todoState, - reducers: { - toggleCompleted: (state, action: PayloadAction) => { - const todo = state.find(todo => todo.id === action.payload) - if (todo) { - todo.completed = !todo.completed - } - }, - - addTodo: (state, action: PayloadAction>) => { - // const newId = state.length > 0 ? state[state.length - 1].id + 1 : 0 - const newId = nextTodoId++ - state.push({ - ...action.payload, - id: newId, - completed: false - }) - }, - - removeTodo: (state, action: PayloadAction) => { - return state.filter(todo => todo.id !== action.payload) - }, - - updateTodo: (state, action: PayloadAction) => { - const index = state.findIndex(todo => todo.id === action.payload.id) - if (index !== -1) { - state[index] = action.payload - } - }, - - clearCompleted: state => { - return state.filter(todo => !todo.completed) - } - } -}) - -const alertSlice = createSlice({ - name: 'alerts', - initialState: alertState, - reducers: { - markAsRead: (state, action: PayloadAction) => { - const alert = state.find(alert => alert.id === action.payload) - if (alert) { - alert.read = true - } - }, - - toggleRead: (state, action: PayloadAction) => { - const alert = state.find(alert => alert.id === action.payload) - if (alert) { - alert.read = !alert.read - } - }, - - addAlert: (state, action: PayloadAction>) => { - const newId = state.length > 0 ? state[state.length - 1].id + 1 : 0 - state.push({ - ...action.payload, - id: newId - }) - }, - - removeAlert: (state, action: PayloadAction) => { - return state.filter(alert => alert.id !== action.payload) - } - } -}) - -const userSlice = createSlice({ - name: 'users', - initialState: userState, - reducers: { - setUserName: (state, action: PayloadAction) => { - state.user.details.name = action.payload - }, - - setUserEmail: (state, action: PayloadAction) => { - state.user.details.email = action.payload - }, - - setAppTheme: (state, action: PayloadAction) => { - state.appSettings.theme = action.payload - }, - - updateUserStatus: (state, action: PayloadAction) => { - state.user.status = action.payload - }, - - updateLoginDetails: ( - state, - action: PayloadAction<{ lastLogin: string; loginCount: number }> - ) => { - state.user.login = { ...state.user.login, ...action.payload } - }, - - updateUserAddress: (state, action: PayloadAction
) => { - state.user.details.address = { - ...state.user.details.address, - ...action.payload - } - }, - - updateBillingAddress: (state, action: PayloadAction) => { - state.user.details.address.billing = { - ...state.user.details.address.billing, - ...action.payload - } - }, - - toggleNewsletterSubscription: state => { - state.user.details.preferences.newsletter = - !state.user.details.preferences.newsletter - }, - - setNotificationPreferences: ( - state, - action: PayloadAction - ) => { - state.user.details.preferences.notifications = { - ...state.user.details.preferences.notifications, - ...action.payload - } - }, - - updateAppLanguage: (state, action: PayloadAction) => { - state.appSettings.language = action.payload - } - } -}) - -const rootReducer = combineReducers({ - [todoSlice.name]: todoSlice.reducer, - [alertSlice.name]: alertSlice.reducer, - [userSlice.name]: userSlice.reducer -}) - -export const setupStore = (preloadedState?: Partial) => { - return configureStore({ reducer: rootReducer, preloadedState }) -} - -export type AppStore = Simplify> - -export type RootState = ReturnType - -export interface LocalTestContext { - store: AppStore - state: RootState -} - -export const { markAsRead, addAlert, removeAlert, toggleRead } = - alertSlice.actions - -export const { - toggleCompleted, - addTodo, - removeTodo, - updateTodo, - clearCompleted -} = todoSlice.actions - -export const { setUserName, setUserEmail, setAppTheme } = userSlice.actions - -// Since Node 16 does not support `structuredClone` -export const deepClone = (object: T): T => - JSON.parse(JSON.stringify(object)) - -export const setFunctionName = (func: AnyFunction, name: string) => { - Object.defineProperty(func, 'name', { value: name }) -} - -export const setFunctionNames = (funcObject: Record) => { - Object.entries(funcObject).forEach(([key, value]) => - setFunctionName(value, key) - ) -} - -const store = setupStore() -const state = store.getState() - -export const localTest = test.extend({ - store, - state -}) - -export const resetSelector = (selector: S) => { - selector.clearCache() - selector.resetRecomputations() - selector.resetDependencyRecomputations() - selector.memoizedResultFunc.clearCache() -} - -export const logRecomputations = (selector: S) => { - console.log( - `${selector.name} result function recalculated:`, - selector.recomputations(), - `time(s)`, - `input selectors recalculated:`, - selector.dependencyRecomputations(), - `time(s)` - ) -} - -export const logSelectorRecomputations = ( - selector: S -) => { - console.log(`\x1B[32m\x1B[1m${selector.name}\x1B[0m:`, { - resultFunc: selector.recomputations(), - inputSelectors: selector.dependencyRecomputations(), - newResults: - typeof selector.memoizedResultFunc.resultsCount === 'function' - ? selector.memoizedResultFunc.resultsCount() - : undefined - }) - // console.log( - // `\x1B[32m\x1B[1m${selector.name}\x1B[0m result function recalculated:`, - // `\x1B[33m${selector.recomputations().toLocaleString('en-US')}\x1B[0m`, - // 'time(s)', - // `input selectors recalculated:`, - // `\x1B[33m${selector - // .dependencyRecomputations() - // .toLocaleString('en-US')}\x1B[0m`, - // 'time(s)' - // ) -} - -export const logFunctionInfo = (func: AnyFunction, recomputations: number) => { - console.log( - `\x1B[32m\x1B[1m${func.name}\x1B[0m was called:`, - recomputations, - 'time(s)' - ) -} - -export const safeApply = ( - func: (...args: Params) => Result, - args: Params -) => func.apply(null, args) - -export const countRecomputations = < - Params extends any[], - Result, - AdditionalFields ->( - func: ((...args: Params) => Result) & AdditionalFields -) => { - let recomputations = 0 - const wrapper = (...args: Params) => { - recomputations++ - return safeApply(func, args) - } - return Object.assign( - wrapper, - { - recomputations: () => recomputations, - resetRecomputations: () => (recomputations = 0) - }, - func - ) -} - -export const runMultipleTimes = ( - func: (...args: Params) => any, - times: number, - ...args: Params -) => { - for (let i = 0; i < times; i++) { - safeApply(func, args) - } -} - -export const expensiveComputation = (times = 1_000_000) => { - for (let i = 0; i < times; i++) { - // Do nothing - } -} - -export const setEnvToProd = () => { - vi.stubEnv('NODE_ENV', 'production') - return vi.unstubAllEnvs -} - -export const isMemoizedSelector = (selector: object) => { - return ( - typeof selector === 'function' && - 'resultFunc' in selector && - 'memoizedResultFunc' in selector && - 'lastResult' in selector && - 'dependencies' in selector && - 'recomputations' in selector && - 'dependencyRecomputations' in selector && - 'resetRecomputations' in selector && - 'resetDependencyRecomputations' in selector && - 'memoize' in selector && - 'argsMemoize' in selector && - typeof selector.resultFunc === 'function' && - typeof selector.memoizedResultFunc === 'function' && - typeof selector.lastResult === 'function' && - Array.isArray(selector.dependencies) && - typeof selector.recomputations === 'function' && - typeof selector.dependencyRecomputations === 'function' && - typeof selector.resetRecomputations === 'function' && - typeof selector.resetDependencyRecomputations === 'function' && - typeof selector.memoize === 'function' && - typeof selector.argsMemoize === 'function' && - selector.dependencies.length >= 1 && - selector.dependencies.every( - (dependency): dependency is Function => typeof dependency === 'function' - ) && - !selector.lastResult.length && - !selector.recomputations.length && - !selector.resetRecomputations.length && - typeof selector.recomputations() === 'number' && - typeof selector.dependencyRecomputations() === 'number' - ) -} +import type { PayloadAction } from '@reduxjs/toolkit' +import { combineReducers, configureStore, createSlice } from '@reduxjs/toolkit' +import { test } from 'vitest' +import type { AnyFunction, OutputSelector, Simplify } from '../src/types' + +export interface Todo { + id: number + title: string + description: string + completed: boolean +} + +interface Alert { + id: number + message: string + type: string + read: boolean +} + +interface BillingAddress { + street: string + city: string + state: string + zip: string +} + +interface Address extends BillingAddress { + billing: BillingAddress +} + +interface PushNotification { + enabled: boolean + frequency: string +} + +interface Notifications { + email: boolean + sms: boolean + push: PushNotification +} + +interface Preferences { + newsletter: boolean + notifications: Notifications +} + +interface Login { + lastLogin: string + loginCount: number +} + +interface UserDetails { + name: string + email: string + address: Address + preferences: Preferences +} + +interface User { + id: number + details: UserDetails + status: string + login: Login +} + +interface AppSettings { + theme: string + language: string +} + +interface UserState { + user: User + appSettings: AppSettings +} + +let nextTodoId = 0 + +// For long arrays +const todoState = [ + { + id: nextTodoId++, + title: 'Buy groceries', + description: 'Milk, bread, eggs, and fruits', + completed: false, + }, + { + id: nextTodoId++, + title: 'Schedule dentist appointment', + description: 'Check available slots for next week', + completed: false, + }, + { + id: nextTodoId++, + title: 'Convince the cat to get a job', + description: 'Need extra income for cat treats', + completed: false, + }, + { + id: nextTodoId++, + title: 'Figure out if plants are plotting world domination', + description: 'That cactus looks suspicious...', + completed: false, + }, + { + id: nextTodoId++, + title: 'Practice telekinesis', + description: 'Try moving the remote without getting up', + completed: false, + }, + { + id: nextTodoId++, + title: 'Determine location of El Dorado', + description: 'Might need it for the next vacation', + completed: false, + }, + { + id: nextTodoId++, + title: 'Master the art of invisible potato juggling', + description: 'Great party trick', + completed: false, + }, +] + +export const createTodoItem = () => { + const id = nextTodoId++ + return { + id, + title: `Task ${id}`, + description: `Description for task ${id}`, + completed: false, + } +} + +export const pushToTodos = (limit: number) => { + const { length: todoStateLength } = todoState + // const limit = howMany + todoStateLength + for (let i = todoStateLength; i < limit; i++) { + todoState.push(createTodoItem()) + } +} + +pushToTodos(200) + +const alertState = [ + { + id: 0, + message: 'You have an upcoming meeting at 3 PM.', + type: 'reminder', + read: false, + }, + { + id: 1, + message: 'New software update available.', + type: 'notification', + read: false, + }, + { + id: 3, + message: + 'The plants have been watered, but keep an eye on that shifty cactus.', + type: 'notification', + read: false, + }, + { + id: 4, + message: + 'Telekinesis class has been moved to 5 PM. Please do not bring any spoons.', + type: 'reminder', + read: false, + }, + { + id: 5, + message: + 'Expedition to El Dorado is postponed. The treasure map is being updated.', + type: 'notification', + read: false, + }, + { + id: 6, + message: + 'Invisible potato juggling championship is tonight. May the best mime win.', + type: 'reminder', + read: false, + }, +] + +// For nested fields tests +const userState: UserState = { + user: { + id: 0, + details: { + name: 'John Doe', + email: 'john.doe@example.com', + address: { + street: '123 Main St', + city: 'AnyTown', + state: 'CA', + zip: '12345', + billing: { + street: '456 Main St', + city: 'AnyTown', + state: 'CA', + zip: '12345', + }, + }, + preferences: { + newsletter: true, + notifications: { + email: true, + sms: false, + push: { + enabled: true, + frequency: 'daily', + }, + }, + }, + }, + status: 'active', + login: { + lastLogin: '2023-04-30T12:34:56Z', + loginCount: 123, + }, + }, + appSettings: { + theme: 'dark', + language: 'en-US', + }, +} + +const todoSlice = createSlice({ + name: 'todos', + initialState: todoState, + reducers: { + toggleCompleted: (state, action: PayloadAction) => { + const todo = state.find(todo => todo.id === action.payload) + if (todo) { + todo.completed = !todo.completed + } + }, + + addTodo: (state, action: PayloadAction>) => { + // const newId = state.length > 0 ? state[state.length - 1].id + 1 : 0 + const newId = nextTodoId++ + state.push({ + ...action.payload, + id: newId, + completed: false, + }) + }, + + removeTodo: (state, action: PayloadAction) => { + return state.filter(todo => todo.id !== action.payload) + }, + + updateTodo: (state, action: PayloadAction) => { + const index = state.findIndex(todo => todo.id === action.payload.id) + if (index !== -1) { + state[index] = action.payload + } + }, + + clearCompleted: state => { + return state.filter(todo => !todo.completed) + }, + }, +}) + +const alertSlice = createSlice({ + name: 'alerts', + initialState: alertState, + reducers: { + markAsRead: (state, action: PayloadAction) => { + const alert = state.find(alert => alert.id === action.payload) + if (alert) { + alert.read = true + } + }, + + toggleRead: (state, action: PayloadAction) => { + const alert = state.find(alert => alert.id === action.payload) + if (alert) { + alert.read = !alert.read + } + }, + + addAlert: (state, action: PayloadAction>) => { + const newId = state.length > 0 ? state[state.length - 1].id + 1 : 0 + state.push({ + ...action.payload, + id: newId, + }) + }, + + removeAlert: (state, action: PayloadAction) => { + return state.filter(alert => alert.id !== action.payload) + }, + }, +}) + +const userSlice = createSlice({ + name: 'users', + initialState: userState, + reducers: { + setUserName: (state, action: PayloadAction) => { + state.user.details.name = action.payload + }, + + setUserEmail: (state, action: PayloadAction) => { + state.user.details.email = action.payload + }, + + setAppTheme: (state, action: PayloadAction) => { + state.appSettings.theme = action.payload + }, + + updateUserStatus: (state, action: PayloadAction) => { + state.user.status = action.payload + }, + + updateLoginDetails: ( + state, + action: PayloadAction<{ lastLogin: string; loginCount: number }>, + ) => { + state.user.login = { ...state.user.login, ...action.payload } + }, + + updateUserAddress: (state, action: PayloadAction
) => { + state.user.details.address = { + ...state.user.details.address, + ...action.payload, + } + }, + + updateBillingAddress: (state, action: PayloadAction) => { + state.user.details.address.billing = { + ...state.user.details.address.billing, + ...action.payload, + } + }, + + toggleNewsletterSubscription: state => { + state.user.details.preferences.newsletter = + !state.user.details.preferences.newsletter + }, + + setNotificationPreferences: ( + state, + action: PayloadAction, + ) => { + state.user.details.preferences.notifications = { + ...state.user.details.preferences.notifications, + ...action.payload, + } + }, + + updateAppLanguage: (state, action: PayloadAction) => { + state.appSettings.language = action.payload + }, + }, +}) + +const rootReducer = combineReducers({ + [todoSlice.name]: todoSlice.reducer, + [alertSlice.name]: alertSlice.reducer, + [userSlice.name]: userSlice.reducer, +}) + +export const setupStore = (preloadedState?: Partial) => { + return configureStore({ reducer: rootReducer, preloadedState }) +} + +export type AppStore = Simplify> + +export type RootState = ReturnType + +export interface LocalTestContext { + store: AppStore + state: RootState +} + +export const { markAsRead, addAlert, removeAlert, toggleRead } = + alertSlice.actions + +export const { + toggleCompleted, + addTodo, + removeTodo, + updateTodo, + clearCompleted, +} = todoSlice.actions + +export const { setUserName, setUserEmail, setAppTheme } = userSlice.actions + +// Since Node 16 does not support `structuredClone` +export const deepClone = (object: T): T => + JSON.parse(JSON.stringify(object)) + +export const setFunctionName = (func: AnyFunction, name: string) => { + Object.defineProperty(func, 'name', { value: name }) +} + +export const setFunctionNames = (funcObject: Record) => { + Object.entries(funcObject).forEach(([key, value]) => + setFunctionName(value, key), + ) +} + +const store = setupStore() +const state = store.getState() + +export const localTest = test.extend({ + store, + state, +}) + +export const resetSelector = (selector: S) => { + selector.clearCache() + selector.resetRecomputations() + selector.resetDependencyRecomputations() + selector.memoizedResultFunc.clearCache() +} + +export const logRecomputations = (selector: S) => { + console.log( + `${selector.name} result function recalculated:`, + selector.recomputations(), + `time(s)`, + `input selectors recalculated:`, + selector.dependencyRecomputations(), + `time(s)`, + ) +} + +export const logSelectorRecomputations = ( + selector: S, +) => { + console.log(`\x1B[32m\x1B[1m${selector.name}\x1B[0m:`, { + resultFunc: selector.recomputations(), + inputSelectors: selector.dependencyRecomputations(), + newResults: + typeof selector.memoizedResultFunc.resultsCount === 'function' + ? selector.memoizedResultFunc.resultsCount() + : undefined, + }) + // console.log( + // `\x1B[32m\x1B[1m${selector.name}\x1B[0m result function recalculated:`, + // `\x1B[33m${selector.recomputations().toLocaleString('en-US')}\x1B[0m`, + // 'time(s)', + // `input selectors recalculated:`, + // `\x1B[33m${selector + // .dependencyRecomputations() + // .toLocaleString('en-US')}\x1B[0m`, + // 'time(s)' + // ) +} + +export const logFunctionInfo = (func: AnyFunction, recomputations: number) => { + console.log( + `\x1B[32m\x1B[1m${func.name}\x1B[0m was called:`, + recomputations, + 'time(s)', + ) +} + +export const safeApply = ( + func: (...args: Params) => Result, + args: Params, +) => func.apply(null, args) + +export const countRecomputations = < + Params extends any[], + Result, + AdditionalFields, +>( + func: ((...args: Params) => Result) & AdditionalFields, +) => { + let recomputations = 0 + const wrapper = (...args: Params) => { + recomputations++ + return safeApply(func, args) + } + return Object.assign( + wrapper, + { + recomputations: () => recomputations, + resetRecomputations: () => (recomputations = 0), + }, + func, + ) +} + +export const runMultipleTimes = ( + func: (...args: Params) => any, + times: number, + ...args: Params +) => { + for (let i = 0; i < times; i++) { + safeApply(func, args) + } +} + +export const expensiveComputation = (times = 1_000_000) => { + for (let i = 0; i < times; i++) { + // Do nothing + } +} + +export const setEnvToProd = () => { + vi.stubEnv('NODE_ENV', 'production') + return vi.unstubAllEnvs +} + +export const isMemoizedSelector = (selector: object) => { + return ( + typeof selector === 'function' && + 'resultFunc' in selector && + 'memoizedResultFunc' in selector && + 'lastResult' in selector && + 'dependencies' in selector && + 'recomputations' in selector && + 'dependencyRecomputations' in selector && + 'resetRecomputations' in selector && + 'resetDependencyRecomputations' in selector && + 'memoize' in selector && + 'argsMemoize' in selector && + typeof selector.resultFunc === 'function' && + typeof selector.memoizedResultFunc === 'function' && + typeof selector.lastResult === 'function' && + Array.isArray(selector.dependencies) && + typeof selector.recomputations === 'function' && + typeof selector.dependencyRecomputations === 'function' && + typeof selector.resetRecomputations === 'function' && + typeof selector.resetDependencyRecomputations === 'function' && + typeof selector.memoize === 'function' && + typeof selector.argsMemoize === 'function' && + selector.dependencies.length >= 1 && + selector.dependencies.every( + (dependency): dependency is Function => typeof dependency === 'function', + ) && + !selector.lastResult.length && + !selector.recomputations.length && + !selector.resetRecomputations.length && + typeof selector.recomputations() === 'number' && + typeof selector.dependencyRecomputations() === 'number' + ) +} diff --git a/test/weakmapMemoize.spec.ts b/test/weakmapMemoize.spec.ts index c504acab9..b09dcc3b6 100644 --- a/test/weakmapMemoize.spec.ts +++ b/test/weakmapMemoize.spec.ts @@ -3,7 +3,7 @@ import { createSelector, createSelectorCreator, referenceEqualityCheck, - weakMapMemoize + weakMapMemoize, } from 'reselect' import type { RootState } from './testUtils' import { localTest, setEnvToProd, toggleCompleted } from './testUtils' @@ -39,7 +39,7 @@ describe('Basic selector behavior with weakMapMemoize', () => { const selector = createSelector( (state: StateA) => state.a, a => a, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const firstState = { a: 1 } const firstStateNewPointer = { a: 1 } @@ -58,7 +58,7 @@ describe('Basic selector behavior with weakMapMemoize', () => { const selector = createSelector( (...params: any[]) => params.length, a => a, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) expect(selector({})).toBe(1) }) @@ -67,7 +67,7 @@ describe('Basic selector behavior with weakMapMemoize', () => { const selector = createSelector( (state: StateAB) => state.a, (state: StateAB) => state.b, - (a, b) => a + b + (a, b) => a + b, ) const state1 = { a: 1, b: 2 } expect(selector(state1)).toBe(3) @@ -88,24 +88,24 @@ describe('Basic selector behavior with weakMapMemoize', () => { return state.b }, 'not a function', - (a: any, b: any) => a + b - ) + (a: any, b: any) => a + b, + ), ).toThrow( - 'createSelector expects all input-selectors to be functions, but received the following types: [function unnamed(), function input2(), string]' + 'createSelector expects all input-selectors to be functions, but received the following types: [function unnamed(), function input2(), string]', ) expect(() => // @ts-ignore - createSelector((state: StateAB) => state.a, 'not a function') + createSelector((state: StateAB) => state.a, 'not a function'), ).toThrow( - 'createSelector expects an output function after the inputs, but received: [string]' + 'createSelector expects an output function after the inputs, but received: [string]', ) }) test('memoized composite arguments', () => { const selector = createSelector( (state: StateSub) => state.sub, - sub => sub.a + sub => sub.a, ) const state1 = { sub: { a: 1 } } expect(selector(state1)).toEqual(1) @@ -121,7 +121,7 @@ describe('Basic selector behavior with weakMapMemoize', () => { [state => state.a, state => state.b], (a, b) => { return a + b - } + }, ) expect(selector({ a: 1, b: 2 })).toBe(3) expect(selector({ a: 1, b: 2 })).toBe(3) @@ -139,7 +139,7 @@ describe('Basic selector behavior with weakMapMemoize', () => { (a, b, c) => { called++ return a + b + c - } + }, ) expect(selector({ a: 1, b: 2 }, { c: 100 })).toBe(103) }) @@ -151,7 +151,7 @@ describe('Basic selector behavior with weakMapMemoize', () => { () => { called++ throw Error('test error') - } + }, ) expect(() => selector({ a: 1 })).toThrow('test error') expect(() => selector({ a: 1 })).toThrow('test error') @@ -167,7 +167,7 @@ describe('Basic selector behavior with weakMapMemoize', () => { if (a > 1) throw Error('test error') return a }, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const state1 = { a: 1 } const state2 = { a: 2 } @@ -189,7 +189,7 @@ describe.skipIf(isCoverage)('weakmapMemoize performance tests', () => { (state: StateAB) => state.a, (state: StateAB) => state.b, (a, b) => a + b, - { devModeChecks: { identityFunctionCheck: 'never' } } + { devModeChecks: { identityFunctionCheck: 'never' } }, ) const state1 = { a: 1, b: 2 } @@ -213,9 +213,9 @@ describe.skipIf(isCoverage)('weakmapMemoize performance tests', () => { { devModeChecks: { identityFunctionCheck: 'never', - inputStabilityCheck: 'never' - } - } + inputStabilityCheck: 'never', + }, + }, ) const start = performance.now() @@ -248,7 +248,7 @@ describe('weakMapMemoize integration with resultEqualityCheck', () => { ({ store }) => { const selectTodoIds = weakMapMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck } + { resultEqualityCheck }, ) const firstResult = selectTodoIds(store.getState()) @@ -260,7 +260,7 @@ describe('weakMapMemoize integration with resultEqualityCheck', () => { expect(firstResult).toBe(secondResult) expect(selectTodoIds.resultsCount()).toBe(1) - } + }, ) localTest( @@ -271,8 +271,8 @@ describe('weakMapMemoize integration with resultEqualityCheck', () => { todos => todos.map(({ id }) => id), { memoizeOptions: { resultEqualityCheck }, - devModeChecks: { inputStabilityCheck: 'once' } - } + devModeChecks: { inputStabilityCheck: 'once' }, + }, ) expect(selectTodoIds(store.getState())).to.be.an('array').that.is.not @@ -307,7 +307,7 @@ describe('weakMapMemoize integration with resultEqualityCheck', () => { expect(selectTodoIds.resultsCount()).toBe(3) expect(selectTodoIds.dependencyRecomputations()).toBe(3) - } + }, ) localTest( @@ -319,7 +319,7 @@ describe('weakMapMemoize integration with resultEqualityCheck', () => { const selectTodoIdsWithResultEqualityCheck = weakMapMemoize( (state: RootState) => state.todos.map(({ id }) => id), - { resultEqualityCheck } + { resultEqualityCheck }, ) const firstResultWithResultEqualityCheck = @@ -333,17 +333,17 @@ describe('weakMapMemoize integration with resultEqualityCheck', () => { selectTodoIdsWithResultEqualityCheck(store.getState()) expect(firstResultWithResultEqualityCheck).not.toBe( - secondResultWithResultEqualityCheck + secondResultWithResultEqualityCheck, ) expect(firstResultWithResultEqualityCheck).toStrictEqual( - secondResultWithResultEqualityCheck + secondResultWithResultEqualityCheck, ) expect(selectTodoIdsWithResultEqualityCheck.resultsCount()).toBe(2) const selectTodoIds = weakMapMemoize((state: RootState) => - state.todos.map(({ id }) => id) + state.todos.map(({ id }) => id), ) const firstResult = selectTodoIds(store.getState()) @@ -359,6 +359,6 @@ describe('weakMapMemoize integration with resultEqualityCheck', () => { expect(selectTodoIds.resultsCount()).toBe(2) resultEqualityCheck.mockClear() - } + }, ) }) diff --git a/tsup.config.ts b/tsup.config.ts index 4831184ba..784276665 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -11,19 +11,19 @@ if (process.env.NODE_ENV === 'production') { module.exports = require('./reselect.production.min.cjs') } else { module.exports = require('./reselect.development.cjs') -}` +}`, ) } export default defineConfig((options): Options[] => { const commonOptions: Options = { entry: { - reselect: 'src/index.ts' + reselect: 'src/index.ts', }, sourcemap: true, target: ['esnext'], clean: true, - ...options + ...options, } return [ @@ -32,7 +32,7 @@ export default defineConfig((options): Options[] => { name: 'Modern ESM', target: ['esnext'], format: ['esm'], - outExtension: () => ({ js: '.mjs' }) + outExtension: () => ({ js: '.mjs' }), }, // Support Webpack 4 by pointing `"module"` to a file with a `.js` extension @@ -41,11 +41,11 @@ export default defineConfig((options): Options[] => { ...commonOptions, name: 'Legacy ESM, Webpack 4', entry: { - 'reselect.legacy-esm': 'src/index.ts' + 'reselect.legacy-esm': 'src/index.ts', }, format: ['esm'], outExtension: () => ({ js: '.js' }), - target: ['es2017'] + target: ['es2017'], }, // Meant to be served up via CDNs like `unpkg`. @@ -53,37 +53,37 @@ export default defineConfig((options): Options[] => { ...commonOptions, name: 'Browser-ready ESM', entry: { - 'reselect.browser': 'src/index.ts' + 'reselect.browser': 'src/index.ts', }, platform: 'browser', env: { - NODE_ENV: 'production' + NODE_ENV: 'production', }, format: ['esm'], outExtension: () => ({ js: '.mjs' }), - minify: true + minify: true, }, { ...commonOptions, name: 'CJS Development', entry: { - 'reselect.development': 'src/index.ts' + 'reselect.development': 'src/index.ts', }, env: { - NODE_ENV: 'development' + NODE_ENV: 'development', }, format: ['cjs'], outDir: './dist/cjs/', - outExtension: () => ({ js: '.cjs' }) + outExtension: () => ({ js: '.cjs' }), }, { ...commonOptions, name: 'CJS production', entry: { - 'reselect.production.min': 'src/index.ts' + 'reselect.production.min': 'src/index.ts', }, env: { - NODE_ENV: 'production' + NODE_ENV: 'production', }, format: ['cjs'], outDir: './dist/cjs/', @@ -91,13 +91,13 @@ export default defineConfig((options): Options[] => { minify: true, onSuccess: async () => { await writeCommonJSEntry() - } + }, }, { ...commonOptions, name: 'CJS Type Definitions', format: ['cjs'], - dts: { only: true } - } + dts: { only: true }, + }, ] }) diff --git a/type-tests/argsMemoize.test-d.ts b/type-tests/argsMemoize.test-d.ts index f7531f5f2..fee200dce 100644 --- a/type-tests/argsMemoize.test-d.ts +++ b/type-tests/argsMemoize.test-d.ts @@ -1,893 +1,893 @@ -import memoizeOne from 'memoize-one' -import microMemoize from 'micro-memoize' -import { - unstable_autotrackMemoize as autotrackMemoize, - createSelector, - createSelectorCreator, - lruMemoize, - weakMapMemoize -} from 'reselect' -import { assertType, describe, expectTypeOf, test } from 'vitest' - -interface RootState { - todos: { - id: number - completed: boolean - }[] -} - -const state: RootState = { - todos: [ - { id: 0, completed: false }, - { id: 1, completed: false } - ] -} - -describe('memoize and argsMemoize', () => { - test('Override Only Memoize In createSelector', () => { - const selectorDefaultSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize } - ) - const selectorDefaultArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: lruMemoize } - ) - const selectorDefaultArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } } - ) - const selectorDefaultSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } } - ) - const selectorAutotrackSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: autotrackMemoize } - ) - const selectorAutotrackArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: autotrackMemoize } - ) - // @ts-expect-error When memoize is autotrackMemoize, type of memoizeOptions needs to be the same as options args in autotrackMemoize. - const selectorAutotrackArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: autotrackMemoize, memoizeOptions: { maxSize: 2 } } - ) - const selectorAutotrackSeparateInlineArgsWithMemoizeOptions = - // @ts-expect-error When memoize is autotrackMemoize, type of memoizeOptions needs to be the same as options args in autotrackMemoize. - createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: autotrackMemoize, memoizeOptions: { maxSize: 2 } } - ) - const selectorWeakMapSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: weakMapMemoize } - ) - const selectorWeakMapArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: weakMapMemoize } - ) - // @ts-expect-error When memoize is weakMapMemoize, type of memoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } } - ) - // @ts-expect-error When memoize is weakMapMemoize, type of memoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } } - ) - const createSelectorDefault = createSelectorCreator(lruMemoize) - const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) - const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) - const changeMemoizeMethodSelectorDefault = createSelectorDefault( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: weakMapMemoize } - ) - const changeMemoizeMethodSelectorWeakMap = createSelectorWeakMap( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize } - ) - const changeMemoizeMethodSelectorAutotrack = createSelectorAutotrack( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize } - ) - const changeMemoizeMethodSelectorDefaultWithMemoizeOptions = - // @ts-expect-error When memoize is changed to weakMapMemoize or autotrackMemoize, memoizeOptions cannot be the same type as options args in lruMemoize. - createSelectorDefault( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } } - ) - const changeMemoizeMethodSelectorWeakMapWithMemoizeOptions = - createSelectorWeakMap( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } } // When memoize is changed to lruMemoize, memoizeOptions can now be the same type as options args in lruMemoize. - ) - const changeMemoizeMethodSelectorAutotrackWithMemoizeOptions = - createSelectorAutotrack( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } } // When memoize is changed to lruMemoize, memoizeOptions can now be the same type as options args in lruMemoize. - ) - }) - - test('Override Only argsMemoize In createSelector', () => { - const selectorDefaultSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize } - ) - const selectorDefaultArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize } - ) - const selectorDefaultArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - const selectorDefaultSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - const selectorAutotrackSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: autotrackMemoize } - ) - const selectorAutotrackArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { argsMemoize: autotrackMemoize } - ) - // @ts-expect-error When argsMemoize is autotrackMemoize, type of argsMemoizeOptions needs to be the same as options args in autotrackMemoize. - const selectorAutotrackArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { - argsMemoize: autotrackMemoize, - argsMemoizeOptions: { maxSize: 2 } - } - ) - const selectorAutotrackSeparateInlineArgsWithMemoizeOptions = - // @ts-expect-error When argsMemoize is autotrackMemoize, type of argsMemoizeOptions needs to be the same as options args in autotrackMemoize. - createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - argsMemoize: autotrackMemoize, - argsMemoizeOptions: { maxSize: 2 } - } - ) - const selectorWeakMapSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize } - ) - const selectorWeakMapArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize } - ) - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions1 = createSelector( - [ - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id) - ], - { - argsMemoize: weakMapMemoize, - argsMemoizeOptions: { maxSize: 2 } - } - ) - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions2 = createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: weakMapMemoize, - memoizeOptions: { - equalityCheck: - // @ts-expect-error - (a, b) => a === b, - maxSize: 2 - }, - argsMemoizeOptions: { maxSize: 2 } - } - ) - - const createSelectorLruMemoize = createSelectorCreator({ - memoize: lruMemoize - }) - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions3 = - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - createSelectorLruMemoize( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: weakMapMemoize, - // memoizeOptions: [], - memoizeOptions: [ - { - equalityCheck: - // @ts-expect-error - (a, b) => a === b, - maxSize: 2 - } - ], - argsMemoizeOptions: [{ maxSize: 2 }] - } - ) - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions4 = - createSelectorLruMemoize( - // @ts-expect-error - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoizeOptions: [{ isPromise: false }], - argsMemoizeOptions: - // @ts-expect-error - (a, b) => a === b - } - ) - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions5 = - // @ts-expect-error - createSelectorLruMemoize( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { - argsMemoize: weakMapMemoize, - memoizeOptions: [{ isPromise: false }], - argsMemoizeOptions: [] - // argsMemoizeOptions: (a, b) => a === b - } - ) - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions6 = - createSelectorLruMemoize( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { - argsMemoize: weakMapMemoize, - memoize: weakMapMemoize, - memoizeOptions: [], - argsMemoizeOptions: [] - // argsMemoizeOptions: (a, b) => a === b - } - ) - const createSelectorDefault = createSelectorCreator(lruMemoize) - const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) - const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) - const changeMemoizeMethodSelectorDefault = createSelectorDefault( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize } - ) - const changeMemoizeMethodSelectorWeakMap = createSelectorWeakMap( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize } - ) - const changeMemoizeMethodSelectorAutotrack = createSelectorAutotrack( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize } - ) - const changeMemoizeMethodSelectorDefaultWithMemoizeOptions = - // @ts-expect-error When argsMemoize is changed to weakMapMemoize or autotrackMemoize, argsMemoizeOptions cannot be the same type as options args in lruMemoize. - createSelectorDefault( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - const changeMemoizeMethodSelectorWeakMapWithMemoizeOptions = - createSelectorWeakMap( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } } // When argsMemoize is changed to lruMemoize, argsMemoizeOptions can now be the same type as options args in lruMemoize. - ) - const changeMemoizeMethodSelectorAutotrackWithMemoizeOptions = - createSelectorAutotrack( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } } // When argsMemoize is changed to lruMemoize, argsMemoizeOptions can now be the same type as options args in lruMemoize. - ) - }) - - test('Override memoize And argsMemoize In createSelector', () => { - const createSelectorMicroMemoize = createSelectorCreator({ - memoize: microMemoize, - memoizeOptions: [{ isEqual: (a, b) => a === b }], - // memoizeOptions: { isEqual: (a, b) => a === b }, - argsMemoize: microMemoize, - argsMemoizeOptions: { isEqual: (a, b) => a === b } - }) - const selectorMicroMemoize = createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(({ id }) => id) - ) - assertType(selectorMicroMemoize(state)) - // @ts-expect-error - selectorMicroMemoize() - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoize.cache - selectorMicroMemoize.fn() - selectorMicroMemoize.isMemoized - selectorMicroMemoize.options - // @ts-expect-error - selectorMicroMemoize.clearCache() - // Checking existence of fields related to `memoize` - selectorMicroMemoize.memoizedResultFunc.cache - selectorMicroMemoize.memoizedResultFunc.fn() - selectorMicroMemoize.memoizedResultFunc.isMemoized - selectorMicroMemoize.memoizedResultFunc.options - // @ts-expect-error - selectorMicroMemoize.memoizedResultFunc.clearCache() - // Checking existence of fields related to the actual memoized selector - selectorMicroMemoize.dependencies - assertType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoize.dependencies) - assertType(selectorMicroMemoize.lastResult()) - // @ts-expect-error - selectorMicroMemoize.memoizedResultFunc() - assertType( - selectorMicroMemoize.memoizedResultFunc([{ id: 0, completed: true }]) - ) - selectorMicroMemoize.recomputations() - selectorMicroMemoize.resetRecomputations() - // @ts-expect-error - selectorMicroMemoize.resultFunc() - assertType( - selectorMicroMemoize.resultFunc([{ id: 0, completed: true }]) - ) - - // Checking to see if types dynamically change if memoize or argsMemoize are overridden inside `createSelector`. - // `microMemoize` was initially passed into `createSelectorCreator` - // as `memoize` and `argsMemoize`, After overriding them both to `lruMemoize`, - // not only does the type for `memoizeOptions` and `argsMemoizeOptions` change to - // the options parameter of `lruMemoize`, the output selector fields - // also change their type to the return type of `lruMemoize`. - const selectorMicroMemoizeOverridden = createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 2 }, - argsMemoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 3 } - } - ) - assertType(selectorMicroMemoizeOverridden(state)) - // @ts-expect-error - selectorMicroMemoizeOverridden() - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoizeOverridden.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.options - // Checking existence of fields related to `memoize` - selectorMicroMemoizeOverridden.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.memoizedResultFunc.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.memoizedResultFunc.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.memoizedResultFunc.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.memoizedResultFunc.options - // Checking existence of fields related to the actual memoized selector - assertType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeOverridden.dependencies) - assertType( - selectorMicroMemoizeOverridden.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeOverridden.memoizedResultFunc() - selectorMicroMemoizeOverridden.recomputations() - selectorMicroMemoizeOverridden.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeOverridden.resultFunc() - assertType( - selectorMicroMemoizeOverridden.resultFunc([{ id: 0, completed: true }]) - ) - // Making sure the type behavior is consistent when args are passed in as an array. - const selectorMicroMemoizeOverriddenArray = createSelectorMicroMemoize( - [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 2 }, - argsMemoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 3 } - } - ) - assertType(selectorMicroMemoizeOverriddenArray(state)) - // @ts-expect-error - selectorMicroMemoizeOverriddenArray() - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoizeOverriddenArray.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.options - // Checking existence of fields related to `memoize` - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.options - // Checking existence of fields related to the actual memoized selector - assertType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeOverriddenArray.dependencies) - assertType( - selectorMicroMemoizeOverriddenArray.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeOverriddenArray.memoizedResultFunc() - selectorMicroMemoizeOverriddenArray.recomputations() - selectorMicroMemoizeOverriddenArray.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeOverriddenArray.resultFunc() - assertType( - selectorMicroMemoizeOverriddenArray.resultFunc([ - { id: 0, completed: true } - ]) - ) - const selectorMicroMemoizeOverrideArgsMemoizeOnlyWrong = - // @ts-expect-error Because `memoizeOptions` should not contain `resultEqualityCheck`. - createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(({ id }) => id), - { - argsMemoize: lruMemoize, - memoizeOptions: { - isPromise: false, - resultEqualityCheck: - // @ts-expect-error - (a, b) => a === b - }, - argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b } - } - ) - const selectorMicroMemoizeOverrideArgsMemoizeOnly = - createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(({ id }) => id), - { - argsMemoize: lruMemoize, - memoizeOptions: { isPromise: false }, - argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b } - } - ) - assertType(selectorMicroMemoizeOverrideArgsMemoizeOnly(state)) - // @ts-expect-error - selectorMicroMemoizeOverrideArgsMemoizeOnly() - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoizeOverrideArgsMemoizeOnly.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideArgsMemoizeOnly.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideArgsMemoizeOnly.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideArgsMemoizeOnly.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideArgsMemoizeOnly.options - - // Checking existence of fields related to `memoize`, these should still be the same. - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.cache - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.fn() - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.isMemoized - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.options - // @ts-expect-error Note that since we did not override `memoize` in the options object, - // `memoizedResultFunc.clearCache` is still an invalid field access. - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.clearCache() - // Checking existence of fields related to the actual memoized selector - assertType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeOverrideArgsMemoizeOnly.dependencies) - assertType( - selectorMicroMemoizeOverrideArgsMemoizeOnly.lastResult() - ) - assertType( - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc() - selectorMicroMemoizeOverrideArgsMemoizeOnly.recomputations() - selectorMicroMemoizeOverrideArgsMemoizeOnly.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc() - assertType( - selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc([ - { id: 0, completed: true } - ]) - ) - - const selectorMicroMemoizeOverrideMemoizeOnly = createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - memoizeOptions: { resultEqualityCheck: (a, b) => a === b } - } - ) - assertType(selectorMicroMemoizeOverrideMemoizeOnly(state)) - // @ts-expect-error - selectorMicroMemoizeOverrideMemoizeOnly() - - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoizeOverrideMemoizeOnly.cache - selectorMicroMemoizeOverrideMemoizeOnly.fn - selectorMicroMemoizeOverrideMemoizeOnly.isMemoized - selectorMicroMemoizeOverrideMemoizeOnly.options - // @ts-expect-error Note that since we did not override `argsMemoize` in the options object, - // `selector.clearCache` is still an invalid field access. - selectorMicroMemoizeOverrideMemoizeOnly.clearCache() - - // Checking existence of fields related to `memoize` - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.options - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. - - // Checking existence of fields related to the actual memoized selector - assertType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeOverrideMemoizeOnly.dependencies) - assertType(selectorMicroMemoizeOverrideMemoizeOnly.lastResult()) - assertType( - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc() - selectorMicroMemoizeOverrideMemoizeOnly.recomputations() - selectorMicroMemoizeOverrideMemoizeOnly.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeOverrideMemoizeOnly.resultFunc() - assertType( - selectorMicroMemoizeOverrideMemoizeOnly.resultFunc([ - { id: 0, completed: true } - ]) - ) - - const selectorMicroMemoizePartiallyOverridden = - // @ts-expect-error Since `argsMemoize` is set to `lruMemoize`, `argsMemoizeOptions` must match the options object parameter of `lruMemoize` - createSelectorMicroMemoize( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: { - equalityCheck: - // @ts-expect-error - (a, b) => a === b, - maxSize: 2 - }, - argsMemoizeOptions: { isPromise: false } // This field causes a type error since it does not match the options param of `lruMemoize`. - } - ) - const selectorMicroMemoizePartiallyOverridden1 = - // @ts-expect-error Since `argsMemoize` is set to `lruMemoize`, `argsMemoizeOptions` must match the options object parameter of `lruMemoize` - createSelectorMicroMemoize( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: [ - { - equalityCheck: - // @ts-expect-error - (a, b) => a === b, - maxSize: 2 - } - ], - argsMemoizeOptions: [{ isPromise: false }] // This field causes a type error since it does not match the options param of `lruMemoize`. - } - ) - const selectorMicroMemoizePartiallyOverridden2 = createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { - // memoizeOptions: [ - // { - // equalityCheck: - // // @ts-expect-error - // (a, b) => a === b, - // maxSize: 2 - // } - // ], - argsMemoizeOptions: [{ isPromise: false }] - } - ) - - const selectorDefaultParametric = createSelector( - (state: RootState, id: number) => id, - (state: RootState) => state.todos, - (id, todos) => todos.filter(todo => todo.id === id), - { - argsMemoize: microMemoize, - devModeChecks: { inputStabilityCheck: 'never' }, - memoize: memoizeOne, - argsMemoizeOptions: [], - memoizeOptions: [(a, b) => a === b] - } - ) - assertType< - { - id: number - completed: boolean - }[] - >(selectorDefaultParametric(state, 0)) - assertType< - { - id: number - completed: boolean - }[] - >(selectorDefaultParametric(state, 1)) - // @ts-expect-error - selectorDefaultParametric(state) - // @ts-expect-error - selectorDefaultParametric(1) - // @ts-expect-error - selectorDefaultParametric(state, '') - // @ts-expect-error - selectorDefaultParametric(state, 1, 1) - // Checking existence of fields related to `argsMemoize` - // Prior to override, this field did NOT exist. - selectorDefaultParametric.cache - // Prior to override, this field did NOT exist. - selectorDefaultParametric.fn - // Prior to override, this field did NOT exist. - selectorDefaultParametric.isMemoized - // Prior to override, this field did NOT exist. - selectorDefaultParametric.options - // @ts-expect-error Prior to override, this field DID exist. - selectorDefaultParametric.clearCache() - - // Checking existence of fields related to `memoize` - // @ts-expect-error Prior to override, this field DID exist. - selectorDefaultParametric.memoizedResultFunc.clearCache() - // Prior to override, this field did NOT exist. - selectorDefaultParametric.memoizedResultFunc.clear() - - // Checking existence of fields related to the actual memoized selector - assertType< - [ - (state: RootState, id: number) => number, - (state: RootState) => { id: number; completed: boolean }[] - ] - >(selectorDefaultParametric.dependencies) - assertType<{ id: number; completed: boolean }[]>( - selectorDefaultParametric.lastResult() - ) - assertType<{ id: number; completed: boolean }[]>( - selectorDefaultParametric.memoizedResultFunc(0, [ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorDefaultParametric.memoizedResultFunc() - selectorDefaultParametric.recomputations() - selectorDefaultParametric.resetRecomputations() - // @ts-expect-error - selectorDefaultParametric.resultFunc() - assertType<{ id: number; completed: boolean }[]>( - selectorDefaultParametric.resultFunc(0, [{ id: 0, completed: true }]) - ) - }) - - test('memoize And argsMemoize In createSelectorCreator', () => { - // If we don't pass in `argsMemoize`, the type for `argsMemoizeOptions` - // falls back to the options parameter of `lruMemoize`. - const createSelectorArgsMemoizeOptionsFallbackToDefault = - createSelectorCreator({ - memoize: microMemoize, - memoizeOptions: [{ isPromise: false }], - argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b } - }) - const selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault = - createSelectorArgsMemoizeOptionsFallbackToDefault( - (state: RootState) => state.todos, - todos => todos.map(({ id }) => id) - ) - assertType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault(state) - ) - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault() - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.clearCache() - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.cache - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.fn - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.isMemoized - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.options - // Checking existence of fields related to `memoize` - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc - .cache - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc.fn() - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc - .isMemoized - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc - .options - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc.clearCache() - // Checking existence of fields related to the actual memoized selector - assertType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.dependencies) - assertType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.lastResult() - ) - assertType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc( - [{ id: 0, completed: true }] - ) - ) - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc() - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.recomputations() - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc() - assertType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc([ - { id: 0, completed: true } - ]) - ) - expectTypeOf( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoize - ).toEqualTypeOf(microMemoize) - expectTypeOf( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.argsMemoize - ).toEqualTypeOf(weakMapMemoize) - - const createSelectorWithWrongArgsMemoizeOptions = - // @ts-expect-error If we don't pass in `argsMemoize`, the type for `argsMemoizeOptions` falls back to the options parameter of `lruMemoize`. - createSelectorCreator({ - memoize: microMemoize, - memoizeOptions: { isEqual: (a, b) => a === b }, - argsMemoizeOptions: { - isEqual: - // @ts-expect-error implicit any - (a, b) => a === b - } - }) - - // When passing in an options object as the first argument, there should be no other arguments. - const createSelectorWrong = createSelectorCreator( - { - // @ts-expect-error - memoize: microMemoize, - // @ts-expect-error - memoizeOptions: { isEqual: (a, b) => a === b }, - // @ts-expect-error - argsMemoizeOptions: { equalityCheck: (a, b) => a === b } - }, - [] // This causes the error. - ) - }) - - test('autotrackMemoize types', () => { - const selector = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: autotrackMemoize } - ) - selector.memoizedResultFunc.clearCache - }) -}) +import memoizeOne from 'memoize-one' +import microMemoize from 'micro-memoize' +import { + unstable_autotrackMemoize as autotrackMemoize, + createSelector, + createSelectorCreator, + lruMemoize, + weakMapMemoize, +} from 'reselect' +import { assertType, describe, expectTypeOf, test } from 'vitest' + +interface RootState { + todos: { + id: number + completed: boolean + }[] +} + +const state: RootState = { + todos: [ + { id: 0, completed: false }, + { id: 1, completed: false }, + ], +} + +describe('memoize and argsMemoize', () => { + test('Override Only Memoize In createSelector', () => { + const selectorDefaultSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize }, + ) + const selectorDefaultArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: lruMemoize }, + ) + const selectorDefaultArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const selectorDefaultSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const selectorAutotrackSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: autotrackMemoize }, + ) + const selectorAutotrackArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: autotrackMemoize }, + ) + // @ts-expect-error When memoize is autotrackMemoize, type of memoizeOptions needs to be the same as options args in autotrackMemoize. + const selectorAutotrackArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: autotrackMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const selectorAutotrackSeparateInlineArgsWithMemoizeOptions = + // @ts-expect-error When memoize is autotrackMemoize, type of memoizeOptions needs to be the same as options args in autotrackMemoize. + createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: autotrackMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const selectorWeakMapSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: weakMapMemoize }, + ) + const selectorWeakMapArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: weakMapMemoize }, + ) + // @ts-expect-error When memoize is weakMapMemoize, type of memoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } }, + ) + // @ts-expect-error When memoize is weakMapMemoize, type of memoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const createSelectorDefault = createSelectorCreator(lruMemoize) + const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) + const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) + const changeMemoizeMethodSelectorDefault = createSelectorDefault( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: weakMapMemoize }, + ) + const changeMemoizeMethodSelectorWeakMap = createSelectorWeakMap( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize }, + ) + const changeMemoizeMethodSelectorAutotrack = createSelectorAutotrack( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize }, + ) + const changeMemoizeMethodSelectorDefaultWithMemoizeOptions = + // @ts-expect-error When memoize is changed to weakMapMemoize or autotrackMemoize, memoizeOptions cannot be the same type as options args in lruMemoize. + createSelectorDefault( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const changeMemoizeMethodSelectorWeakMapWithMemoizeOptions = + createSelectorWeakMap( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } }, // When memoize is changed to lruMemoize, memoizeOptions can now be the same type as options args in lruMemoize. + ) + const changeMemoizeMethodSelectorAutotrackWithMemoizeOptions = + createSelectorAutotrack( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } }, // When memoize is changed to lruMemoize, memoizeOptions can now be the same type as options args in lruMemoize. + ) + }) + + test('Override Only argsMemoize In createSelector', () => { + const selectorDefaultSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize }, + ) + const selectorDefaultArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize }, + ) + const selectorDefaultArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + const selectorDefaultSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + const selectorAutotrackSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: autotrackMemoize }, + ) + const selectorAutotrackArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { argsMemoize: autotrackMemoize }, + ) + // @ts-expect-error When argsMemoize is autotrackMemoize, type of argsMemoizeOptions needs to be the same as options args in autotrackMemoize. + const selectorAutotrackArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { + argsMemoize: autotrackMemoize, + argsMemoizeOptions: { maxSize: 2 }, + }, + ) + const selectorAutotrackSeparateInlineArgsWithMemoizeOptions = + // @ts-expect-error When argsMemoize is autotrackMemoize, type of argsMemoizeOptions needs to be the same as options args in autotrackMemoize. + createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + argsMemoize: autotrackMemoize, + argsMemoizeOptions: { maxSize: 2 }, + }, + ) + const selectorWeakMapSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize }, + ) + const selectorWeakMapArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize }, + ) + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions1 = createSelector( + [ + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + ], + { + argsMemoize: weakMapMemoize, + argsMemoizeOptions: { maxSize: 2 }, + }, + ) + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions2 = createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: weakMapMemoize, + memoizeOptions: { + equalityCheck: + // @ts-expect-error + (a, b) => a === b, + maxSize: 2, + }, + argsMemoizeOptions: { maxSize: 2 }, + }, + ) + + const createSelectorLruMemoize = createSelectorCreator({ + memoize: lruMemoize, + }) + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions3 = + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + createSelectorLruMemoize( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: weakMapMemoize, + // memoizeOptions: [], + memoizeOptions: [ + { + equalityCheck: + // @ts-expect-error + (a, b) => a === b, + maxSize: 2, + }, + ], + argsMemoizeOptions: [{ maxSize: 2 }], + }, + ) + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions4 = + createSelectorLruMemoize( + // @ts-expect-error + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoizeOptions: [{ isPromise: false }], + argsMemoizeOptions: + // @ts-expect-error + (a, b) => a === b, + }, + ) + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions5 = + // @ts-expect-error + createSelectorLruMemoize( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { + argsMemoize: weakMapMemoize, + memoizeOptions: [{ isPromise: false }], + argsMemoizeOptions: [], + // argsMemoizeOptions: (a, b) => a === b + }, + ) + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions6 = + createSelectorLruMemoize( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { + argsMemoize: weakMapMemoize, + memoize: weakMapMemoize, + memoizeOptions: [], + argsMemoizeOptions: [], + // argsMemoizeOptions: (a, b) => a === b + }, + ) + const createSelectorDefault = createSelectorCreator(lruMemoize) + const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) + const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) + const changeMemoizeMethodSelectorDefault = createSelectorDefault( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize }, + ) + const changeMemoizeMethodSelectorWeakMap = createSelectorWeakMap( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize }, + ) + const changeMemoizeMethodSelectorAutotrack = createSelectorAutotrack( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize }, + ) + const changeMemoizeMethodSelectorDefaultWithMemoizeOptions = + // @ts-expect-error When argsMemoize is changed to weakMapMemoize or autotrackMemoize, argsMemoizeOptions cannot be the same type as options args in lruMemoize. + createSelectorDefault( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + const changeMemoizeMethodSelectorWeakMapWithMemoizeOptions = + createSelectorWeakMap( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } }, // When argsMemoize is changed to lruMemoize, argsMemoizeOptions can now be the same type as options args in lruMemoize. + ) + const changeMemoizeMethodSelectorAutotrackWithMemoizeOptions = + createSelectorAutotrack( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } }, // When argsMemoize is changed to lruMemoize, argsMemoizeOptions can now be the same type as options args in lruMemoize. + ) + }) + + test('Override memoize And argsMemoize In createSelector', () => { + const createSelectorMicroMemoize = createSelectorCreator({ + memoize: microMemoize, + memoizeOptions: [{ isEqual: (a, b) => a === b }], + // memoizeOptions: { isEqual: (a, b) => a === b }, + argsMemoize: microMemoize, + argsMemoizeOptions: { isEqual: (a, b) => a === b }, + }) + const selectorMicroMemoize = createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(({ id }) => id), + ) + assertType(selectorMicroMemoize(state)) + // @ts-expect-error + selectorMicroMemoize() + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoize.cache + selectorMicroMemoize.fn() + selectorMicroMemoize.isMemoized + selectorMicroMemoize.options + // @ts-expect-error + selectorMicroMemoize.clearCache() + // Checking existence of fields related to `memoize` + selectorMicroMemoize.memoizedResultFunc.cache + selectorMicroMemoize.memoizedResultFunc.fn() + selectorMicroMemoize.memoizedResultFunc.isMemoized + selectorMicroMemoize.memoizedResultFunc.options + // @ts-expect-error + selectorMicroMemoize.memoizedResultFunc.clearCache() + // Checking existence of fields related to the actual memoized selector + selectorMicroMemoize.dependencies + assertType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoize.dependencies) + assertType(selectorMicroMemoize.lastResult()) + // @ts-expect-error + selectorMicroMemoize.memoizedResultFunc() + assertType( + selectorMicroMemoize.memoizedResultFunc([{ id: 0, completed: true }]), + ) + selectorMicroMemoize.recomputations() + selectorMicroMemoize.resetRecomputations() + // @ts-expect-error + selectorMicroMemoize.resultFunc() + assertType( + selectorMicroMemoize.resultFunc([{ id: 0, completed: true }]), + ) + + // Checking to see if types dynamically change if memoize or argsMemoize are overridden inside `createSelector`. + // `microMemoize` was initially passed into `createSelectorCreator` + // as `memoize` and `argsMemoize`, After overriding them both to `lruMemoize`, + // not only does the type for `memoizeOptions` and `argsMemoizeOptions` change to + // the options parameter of `lruMemoize`, the output selector fields + // also change their type to the return type of `lruMemoize`. + const selectorMicroMemoizeOverridden = createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 2 }, + argsMemoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 3 }, + }, + ) + assertType(selectorMicroMemoizeOverridden(state)) + // @ts-expect-error + selectorMicroMemoizeOverridden() + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoizeOverridden.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.options + // Checking existence of fields related to `memoize` + selectorMicroMemoizeOverridden.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.memoizedResultFunc.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.memoizedResultFunc.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.memoizedResultFunc.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.memoizedResultFunc.options + // Checking existence of fields related to the actual memoized selector + assertType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeOverridden.dependencies) + assertType( + selectorMicroMemoizeOverridden.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeOverridden.memoizedResultFunc() + selectorMicroMemoizeOverridden.recomputations() + selectorMicroMemoizeOverridden.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeOverridden.resultFunc() + assertType( + selectorMicroMemoizeOverridden.resultFunc([{ id: 0, completed: true }]), + ) + // Making sure the type behavior is consistent when args are passed in as an array. + const selectorMicroMemoizeOverriddenArray = createSelectorMicroMemoize( + [(state: RootState) => state.todos], + todos => todos.map(({ id }) => id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 2 }, + argsMemoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 3 }, + }, + ) + assertType(selectorMicroMemoizeOverriddenArray(state)) + // @ts-expect-error + selectorMicroMemoizeOverriddenArray() + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoizeOverriddenArray.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.options + // Checking existence of fields related to `memoize` + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.options + // Checking existence of fields related to the actual memoized selector + assertType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeOverriddenArray.dependencies) + assertType( + selectorMicroMemoizeOverriddenArray.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeOverriddenArray.memoizedResultFunc() + selectorMicroMemoizeOverriddenArray.recomputations() + selectorMicroMemoizeOverriddenArray.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeOverriddenArray.resultFunc() + assertType( + selectorMicroMemoizeOverriddenArray.resultFunc([ + { id: 0, completed: true }, + ]), + ) + const selectorMicroMemoizeOverrideArgsMemoizeOnlyWrong = + // @ts-expect-error Because `memoizeOptions` should not contain `resultEqualityCheck`. + createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(({ id }) => id), + { + argsMemoize: lruMemoize, + memoizeOptions: { + isPromise: false, + resultEqualityCheck: + // @ts-expect-error + (a, b) => a === b, + }, + argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }, + ) + const selectorMicroMemoizeOverrideArgsMemoizeOnly = + createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(({ id }) => id), + { + argsMemoize: lruMemoize, + memoizeOptions: { isPromise: false }, + argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }, + ) + assertType(selectorMicroMemoizeOverrideArgsMemoizeOnly(state)) + // @ts-expect-error + selectorMicroMemoizeOverrideArgsMemoizeOnly() + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoizeOverrideArgsMemoizeOnly.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideArgsMemoizeOnly.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideArgsMemoizeOnly.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideArgsMemoizeOnly.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideArgsMemoizeOnly.options + + // Checking existence of fields related to `memoize`, these should still be the same. + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.cache + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.fn() + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.isMemoized + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.options + // @ts-expect-error Note that since we did not override `memoize` in the options object, + // `memoizedResultFunc.clearCache` is still an invalid field access. + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.clearCache() + // Checking existence of fields related to the actual memoized selector + assertType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeOverrideArgsMemoizeOnly.dependencies) + assertType( + selectorMicroMemoizeOverrideArgsMemoizeOnly.lastResult(), + ) + assertType( + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc() + selectorMicroMemoizeOverrideArgsMemoizeOnly.recomputations() + selectorMicroMemoizeOverrideArgsMemoizeOnly.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc() + assertType( + selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc([ + { id: 0, completed: true }, + ]), + ) + + const selectorMicroMemoizeOverrideMemoizeOnly = createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + memoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }, + ) + assertType(selectorMicroMemoizeOverrideMemoizeOnly(state)) + // @ts-expect-error + selectorMicroMemoizeOverrideMemoizeOnly() + + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoizeOverrideMemoizeOnly.cache + selectorMicroMemoizeOverrideMemoizeOnly.fn + selectorMicroMemoizeOverrideMemoizeOnly.isMemoized + selectorMicroMemoizeOverrideMemoizeOnly.options + // @ts-expect-error Note that since we did not override `argsMemoize` in the options object, + // `selector.clearCache` is still an invalid field access. + selectorMicroMemoizeOverrideMemoizeOnly.clearCache() + + // Checking existence of fields related to `memoize` + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.options + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. + + // Checking existence of fields related to the actual memoized selector + assertType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeOverrideMemoizeOnly.dependencies) + assertType(selectorMicroMemoizeOverrideMemoizeOnly.lastResult()) + assertType( + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc() + selectorMicroMemoizeOverrideMemoizeOnly.recomputations() + selectorMicroMemoizeOverrideMemoizeOnly.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeOverrideMemoizeOnly.resultFunc() + assertType( + selectorMicroMemoizeOverrideMemoizeOnly.resultFunc([ + { id: 0, completed: true }, + ]), + ) + + const selectorMicroMemoizePartiallyOverridden = + // @ts-expect-error Since `argsMemoize` is set to `lruMemoize`, `argsMemoizeOptions` must match the options object parameter of `lruMemoize` + createSelectorMicroMemoize( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: { + equalityCheck: + // @ts-expect-error + (a, b) => a === b, + maxSize: 2, + }, + argsMemoizeOptions: { isPromise: false }, // This field causes a type error since it does not match the options param of `lruMemoize`. + }, + ) + const selectorMicroMemoizePartiallyOverridden1 = + // @ts-expect-error Since `argsMemoize` is set to `lruMemoize`, `argsMemoizeOptions` must match the options object parameter of `lruMemoize` + createSelectorMicroMemoize( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: [ + { + equalityCheck: + // @ts-expect-error + (a, b) => a === b, + maxSize: 2, + }, + ], + argsMemoizeOptions: [{ isPromise: false }], // This field causes a type error since it does not match the options param of `lruMemoize`. + }, + ) + const selectorMicroMemoizePartiallyOverridden2 = createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { + // memoizeOptions: [ + // { + // equalityCheck: + // // @ts-expect-error + // (a, b) => a === b, + // maxSize: 2 + // } + // ], + argsMemoizeOptions: [{ isPromise: false }], + }, + ) + + const selectorDefaultParametric = createSelector( + (state: RootState, id: number) => id, + (state: RootState) => state.todos, + (id, todos) => todos.filter(todo => todo.id === id), + { + argsMemoize: microMemoize, + devModeChecks: { inputStabilityCheck: 'never' }, + memoize: memoizeOne, + argsMemoizeOptions: [], + memoizeOptions: [(a, b) => a === b], + }, + ) + assertType< + { + id: number + completed: boolean + }[] + >(selectorDefaultParametric(state, 0)) + assertType< + { + id: number + completed: boolean + }[] + >(selectorDefaultParametric(state, 1)) + // @ts-expect-error + selectorDefaultParametric(state) + // @ts-expect-error + selectorDefaultParametric(1) + // @ts-expect-error + selectorDefaultParametric(state, '') + // @ts-expect-error + selectorDefaultParametric(state, 1, 1) + // Checking existence of fields related to `argsMemoize` + // Prior to override, this field did NOT exist. + selectorDefaultParametric.cache + // Prior to override, this field did NOT exist. + selectorDefaultParametric.fn + // Prior to override, this field did NOT exist. + selectorDefaultParametric.isMemoized + // Prior to override, this field did NOT exist. + selectorDefaultParametric.options + // @ts-expect-error Prior to override, this field DID exist. + selectorDefaultParametric.clearCache() + + // Checking existence of fields related to `memoize` + // @ts-expect-error Prior to override, this field DID exist. + selectorDefaultParametric.memoizedResultFunc.clearCache() + // Prior to override, this field did NOT exist. + selectorDefaultParametric.memoizedResultFunc.clear() + + // Checking existence of fields related to the actual memoized selector + assertType< + [ + (state: RootState, id: number) => number, + (state: RootState) => { id: number; completed: boolean }[], + ] + >(selectorDefaultParametric.dependencies) + assertType<{ id: number; completed: boolean }[]>( + selectorDefaultParametric.lastResult(), + ) + assertType<{ id: number; completed: boolean }[]>( + selectorDefaultParametric.memoizedResultFunc(0, [ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorDefaultParametric.memoizedResultFunc() + selectorDefaultParametric.recomputations() + selectorDefaultParametric.resetRecomputations() + // @ts-expect-error + selectorDefaultParametric.resultFunc() + assertType<{ id: number; completed: boolean }[]>( + selectorDefaultParametric.resultFunc(0, [{ id: 0, completed: true }]), + ) + }) + + test('memoize And argsMemoize In createSelectorCreator', () => { + // If we don't pass in `argsMemoize`, the type for `argsMemoizeOptions` + // falls back to the options parameter of `lruMemoize`. + const createSelectorArgsMemoizeOptionsFallbackToDefault = + createSelectorCreator({ + memoize: microMemoize, + memoizeOptions: [{ isPromise: false }], + argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }) + const selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault = + createSelectorArgsMemoizeOptionsFallbackToDefault( + (state: RootState) => state.todos, + todos => todos.map(({ id }) => id), + ) + assertType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault(state), + ) + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault() + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.clearCache() + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.cache + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.fn + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.isMemoized + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.options + // Checking existence of fields related to `memoize` + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc + .cache + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc.fn() + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc + .isMemoized + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc + .options + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc.clearCache() + // Checking existence of fields related to the actual memoized selector + assertType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.dependencies) + assertType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.lastResult(), + ) + assertType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc( + [{ id: 0, completed: true }], + ), + ) + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc() + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.recomputations() + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc() + assertType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc([ + { id: 0, completed: true }, + ]), + ) + expectTypeOf( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoize, + ).toEqualTypeOf(microMemoize) + expectTypeOf( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.argsMemoize, + ).toEqualTypeOf(weakMapMemoize) + + const createSelectorWithWrongArgsMemoizeOptions = + // @ts-expect-error If we don't pass in `argsMemoize`, the type for `argsMemoizeOptions` falls back to the options parameter of `lruMemoize`. + createSelectorCreator({ + memoize: microMemoize, + memoizeOptions: { isEqual: (a, b) => a === b }, + argsMemoizeOptions: { + isEqual: + // @ts-expect-error implicit any + (a, b) => a === b, + }, + }) + + // When passing in an options object as the first argument, there should be no other arguments. + const createSelectorWrong = createSelectorCreator( + { + // @ts-expect-error + memoize: microMemoize, + // @ts-expect-error + memoizeOptions: { isEqual: (a, b) => a === b }, + // @ts-expect-error + argsMemoizeOptions: { equalityCheck: (a, b) => a === b }, + }, + [], // This causes the error. + ) + }) + + test('autotrackMemoize types', () => { + const selector = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: autotrackMemoize }, + ) + selector.memoizedResultFunc.clearCache + }) +}) diff --git a/type-tests/createSelector.withTypes.test-d.ts b/type-tests/createSelector.withTypes.test-d.ts index c3e00618e..cb4f35561 100644 --- a/type-tests/createSelector.withTypes.test-d.ts +++ b/type-tests/createSelector.withTypes.test-d.ts @@ -19,12 +19,12 @@ interface RootState { const rootState: RootState = { todos: [ { id: 0, completed: false }, - { id: 1, completed: false } + { id: 1, completed: false }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: false } - ] + { id: 1, read: false }, + ], } describe('createSelector.withTypes()', () => { @@ -33,7 +33,7 @@ describe('createSelector.withTypes()', () => { describe('when input selectors are provided as a single array', () => { test('locks down state type and infers result function parameter types correctly', () => { expectTypeOf(createSelector.withTypes).returns.toEqualTypeOf( - createSelector + createSelector, ) // Type of state is locked and the parameter types of the result function @@ -44,13 +44,13 @@ describe('createSelector.withTypes()', () => { expectTypeOf(state).toEqualTypeOf(rootState) return state.todos - } + }, ], todos => { expectTypeOf(todos).toEqualTypeOf(rootState.todos) return todos.map(({ id }) => id) - } + }, ) }) }) @@ -76,7 +76,7 @@ describe('createSelector.withTypes()', () => { // the parameter types of the result function when // input selectors are provided as a single array. return todos.map(({ id }) => id) - } + }, ) }) @@ -104,14 +104,14 @@ describe('createSelector.withTypes()', () => { // the parameter types of the result function when // input selectors are provided as a single array. return todos.map(({ id }) => id) - } + }, ) }) test('can annotate parameter types of the result function to workaround type inference issue', () => { createAppSelector( state => state.todos, - (todos: Todo[]) => todos.map(({ id }) => id) + (todos: Todo[]) => todos.map(({ id }) => id), ) }) }) diff --git a/type-tests/createSelectorCreator.test-d.ts b/type-tests/createSelectorCreator.test-d.ts index e91205f19..da9b75967 100644 --- a/type-tests/createSelectorCreator.test-d.ts +++ b/type-tests/createSelectorCreator.test-d.ts @@ -1,58 +1,58 @@ -import lodashMemoize from 'lodash/memoize' -import memoizeOne from 'memoize-one' -import microMemoize from 'micro-memoize' -import { - createSelectorCreator, - lruMemoize, - unstable_autotrackMemoize as autotrackMemoize, - weakMapMemoize -} from 'reselect' -import { describe, test } from 'vitest' - -interface RootState { - todos: { id: number; completed: boolean }[] - alerts: { id: number; read: boolean }[] -} - -const state: RootState = { - todos: [ - { id: 0, completed: false }, - { id: 1, completed: true } - ], - alerts: [ - { id: 0, read: false }, - { id: 1, read: true } - ] -} - -describe('createSelectorCreator', () => { - test('options object as argument', () => { - const createSelectorDefault = createSelectorCreator({ - memoize: lruMemoize - }) - const createSelectorWeakMap = createSelectorCreator({ - memoize: weakMapMemoize - }) - const createSelectorAutotrack = createSelectorCreator({ - memoize: autotrackMemoize - }) - const createSelectorMicro = createSelectorCreator({ - memoize: microMemoize - }) - const createSelectorOne = createSelectorCreator({ - memoize: memoizeOne - }) - const createSelectorLodash = createSelectorCreator({ - memoize: lodashMemoize - }) - }) - - test('memoize function as argument', () => { - const createSelectorDefault = createSelectorCreator(lruMemoize) - const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) - const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) - const createSelectorMicro = createSelectorCreator(microMemoize) - const createSelectorOne = createSelectorCreator(memoizeOne) - const createSelectorLodash = createSelectorCreator(lodashMemoize) - }) -}) +import lodashMemoize from 'lodash/memoize' +import memoizeOne from 'memoize-one' +import microMemoize from 'micro-memoize' +import { + createSelectorCreator, + lruMemoize, + unstable_autotrackMemoize as autotrackMemoize, + weakMapMemoize, +} from 'reselect' +import { describe, test } from 'vitest' + +interface RootState { + todos: { id: number; completed: boolean }[] + alerts: { id: number; read: boolean }[] +} + +const state: RootState = { + todos: [ + { id: 0, completed: false }, + { id: 1, completed: true }, + ], + alerts: [ + { id: 0, read: false }, + { id: 1, read: true }, + ], +} + +describe('createSelectorCreator', () => { + test('options object as argument', () => { + const createSelectorDefault = createSelectorCreator({ + memoize: lruMemoize, + }) + const createSelectorWeakMap = createSelectorCreator({ + memoize: weakMapMemoize, + }) + const createSelectorAutotrack = createSelectorCreator({ + memoize: autotrackMemoize, + }) + const createSelectorMicro = createSelectorCreator({ + memoize: microMemoize, + }) + const createSelectorOne = createSelectorCreator({ + memoize: memoizeOne, + }) + const createSelectorLodash = createSelectorCreator({ + memoize: lodashMemoize, + }) + }) + + test('memoize function as argument', () => { + const createSelectorDefault = createSelectorCreator(lruMemoize) + const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) + const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) + const createSelectorMicro = createSelectorCreator(microMemoize) + const createSelectorOne = createSelectorCreator(memoizeOne) + const createSelectorLodash = createSelectorCreator(lodashMemoize) + }) +}) diff --git a/type-tests/createStructuredSelector.test-d.ts b/type-tests/createStructuredSelector.test-d.ts index 864cb44f2..f8342858d 100644 --- a/type-tests/createStructuredSelector.test-d.ts +++ b/type-tests/createStructuredSelector.test-d.ts @@ -1,273 +1,272 @@ -import microMemoize from 'micro-memoize' -import type { Selector, TypedStructuredSelectorCreator } from 'reselect' -import { - createSelectorCreator, - createStructuredSelector, - lruMemoize, - weakMapMemoize -} from 'reselect' -import { describe, expectTypeOf, test } from 'vitest' - -interface Todo { - id: number - completed: boolean -} - -interface Alert { - id: number - read: boolean -} - -interface RootState { - todos: Todo[] - alerts: Alert[] -} - -const rootState: RootState = { - todos: [ - { id: 0, completed: false }, - { id: 1, completed: true } - ], - alerts: [ - { id: 0, read: false }, - { id: 1, read: true } - ] -} - -describe('createStructuredSelector', () => { - - // TODO: Remove this test block once `TypedStructuredSelectorCreator` is removed. - test('TypedStructuredSelectorCreator should lock down state type', () => { - const createStructuredAppSelector: TypedStructuredSelectorCreator = - createStructuredSelector - - const structuredSelector = createStructuredAppSelector({ - todos: state => { - expectTypeOf(state).toEqualTypeOf(rootState) - - return state.todos - }, - alerts: state => { - expectTypeOf(state).toEqualTypeOf(rootState) - - return state.alerts - } - }) - - const { todos, alerts } = structuredSelector(rootState) - - expectTypeOf(todos).toEqualTypeOf() - - expectTypeOf(alerts).toEqualTypeOf() - - expectTypeOf(structuredSelector.argsMemoize).toEqualTypeOf< - typeof weakMapMemoize - >(weakMapMemoize) - - expectTypeOf(structuredSelector.memoize).toEqualTypeOf< - typeof weakMapMemoize - >(weakMapMemoize) - - expectTypeOf(structuredSelector.clearCache).returns.toBeVoid() - - expectTypeOf(structuredSelector.clearCache).parameters.toEqualTypeOf<[]>() - - expectTypeOf(structuredSelector.dependencies).items.toBeFunction() - - expectTypeOf(structuredSelector.dependencyRecomputations).toEqualTypeOf< - () => number - >() - - expectTypeOf(structuredSelector.recomputations).toEqualTypeOf< - () => number - >() - - expectTypeOf( - structuredSelector.resetDependencyRecomputations - ).toEqualTypeOf<() => void>() - - expectTypeOf(structuredSelector.resetRecomputations).toEqualTypeOf< - () => void - >() - - expectTypeOf( - structuredSelector.lastResult - ).returns.toEqualTypeOf(rootState) - - expectTypeOf( - structuredSelector.memoizedResultFunc - ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ - rootState.todos, - rootState.alerts - ]) - - expectTypeOf(structuredSelector.memoizedResultFunc).returns.toEqualTypeOf< - ReturnType - >(structuredSelector.lastResult()) - - expectTypeOf(structuredSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' - ) - - expectTypeOf(structuredSelector.resultFunc).returns.toEqualTypeOf< - ReturnType - >(structuredSelector.lastResult()) - }) - - // TODO: Remove this test block once `TypedStructuredSelectorCreator` is removed. - test('TypedStructuredSelectorCreator should correctly infer memoize and argsMemoize', () => { - const createSelectorLru = createSelectorCreator({ - memoize: lruMemoize, - argsMemoize: microMemoize - }) - - const createStructuredAppSelector: TypedStructuredSelectorCreator = - createStructuredSelector - - const structuredSelector = createStructuredAppSelector( - { - todos: state => state.todos, - alerts: state => state.alerts - }, - createSelectorLru - ) - - expectTypeOf(structuredSelector.argsMemoize).toEqualTypeOf< - typeof microMemoize - >(microMemoize) - - expectTypeOf(structuredSelector.memoize).toEqualTypeOf( - lruMemoize - ) - - const { todos, alerts } = structuredSelector(rootState) - - expectTypeOf(todos).toEqualTypeOf() - - expectTypeOf(alerts).toEqualTypeOf() - - expectTypeOf(structuredSelector.dependencies).items.toBeFunction() - - expectTypeOf(structuredSelector.dependencyRecomputations).toEqualTypeOf< - () => number - >() - - expectTypeOf(structuredSelector.recomputations).toEqualTypeOf< - () => number - >() - - expectTypeOf( - structuredSelector.resetDependencyRecomputations - ).toEqualTypeOf<() => void>() - - expectTypeOf(structuredSelector.resetRecomputations).toEqualTypeOf< - () => void - >() - - expectTypeOf( - structuredSelector.lastResult - ).returns.toEqualTypeOf(rootState) - - expectTypeOf( - structuredSelector.memoizedResultFunc - ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ - rootState.todos, - rootState.alerts - ]) - - expectTypeOf(structuredSelector.memoizedResultFunc).returns.toEqualTypeOf< - ReturnType - >(structuredSelector.lastResult()) - - expectTypeOf(structuredSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' - ) - - expectTypeOf(structuredSelector.resultFunc).returns.toEqualTypeOf< - ReturnType - >(structuredSelector.lastResult()) - }) - - test('supports additional parameters', () => { - const structuredSelector = createStructuredSelector({ - todos: (state: RootState) => state.todos, - alerts: (state: RootState) => state.alerts, - todoById: (state: RootState, id: number) => state.todos[id] - }) - - const { alerts, todos, todoById } = structuredSelector(rootState, 0) - - expectTypeOf(todos).toEqualTypeOf() - - expectTypeOf(alerts).toEqualTypeOf() - - expectTypeOf(todoById).toEqualTypeOf() - - expectTypeOf(structuredSelector.argsMemoize).toEqualTypeOf< - typeof weakMapMemoize - >(weakMapMemoize) - - expectTypeOf(structuredSelector.memoize).toEqualTypeOf< - typeof weakMapMemoize - >(weakMapMemoize) - - expectTypeOf(structuredSelector.clearCache).returns.toBeVoid() - - expectTypeOf(structuredSelector.clearCache).parameters.toEqualTypeOf<[]>() - - expectTypeOf(structuredSelector.dependencies).items.toMatchTypeOf< - Selector - >() - - expectTypeOf(structuredSelector.dependencyRecomputations).toEqualTypeOf< - () => number - >() - - expectTypeOf(structuredSelector.recomputations).toEqualTypeOf< - () => number - >() - - expectTypeOf( - structuredSelector.resetDependencyRecomputations - ).returns.toBeVoid() - - expectTypeOf( - structuredSelector.resetDependencyRecomputations - ).parameters.items.toBeNever() - - expectTypeOf(structuredSelector.resetRecomputations).returns.toBeVoid() - - expectTypeOf( - structuredSelector.resetRecomputations - ).parameters.items.toBeNever() - - // Use `.branded` for intersection types https://github.com/mmkal/expect-type#why-is-my-assertion-failing - expectTypeOf(structuredSelector.lastResult).returns.branded.toEqualTypeOf< - RootState & { todoById: Todo } - >() - - expectTypeOf( - structuredSelector.memoizedResultFunc - ).parameters.toEqualTypeOf<[Todo[], Alert[], Todo]>([ - rootState.todos, - rootState.alerts, - rootState.todos[0] - ]) - - expectTypeOf(structuredSelector.resultFunc).parameters.toEqualTypeOf< - [Todo[], Alert[], Todo] - >([rootState.todos, rootState.alerts, rootState.todos[0]]) - - expectTypeOf(structuredSelector.memoizedResultFunc).returns.toEqualTypeOf< - ReturnType - >(structuredSelector.lastResult()) - - expectTypeOf(structuredSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' - ) - - expectTypeOf(structuredSelector.resultFunc).returns.toEqualTypeOf< - ReturnType - >(structuredSelector.lastResult()) - }) -}) +import microMemoize from 'micro-memoize' +import type { Selector, TypedStructuredSelectorCreator } from 'reselect' +import { + createSelectorCreator, + createStructuredSelector, + lruMemoize, + weakMapMemoize, +} from 'reselect' +import { describe, expectTypeOf, test } from 'vitest' + +interface Todo { + id: number + completed: boolean +} + +interface Alert { + id: number + read: boolean +} + +interface RootState { + todos: Todo[] + alerts: Alert[] +} + +const rootState: RootState = { + todos: [ + { id: 0, completed: false }, + { id: 1, completed: true }, + ], + alerts: [ + { id: 0, read: false }, + { id: 1, read: true }, + ], +} + +describe('createStructuredSelector', () => { + // TODO: Remove this test block once `TypedStructuredSelectorCreator` is removed. + test('TypedStructuredSelectorCreator should lock down state type', () => { + const createStructuredAppSelector: TypedStructuredSelectorCreator = + createStructuredSelector + + const structuredSelector = createStructuredAppSelector({ + todos: state => { + expectTypeOf(state).toEqualTypeOf(rootState) + + return state.todos + }, + alerts: state => { + expectTypeOf(state).toEqualTypeOf(rootState) + + return state.alerts + }, + }) + + const { todos, alerts } = structuredSelector(rootState) + + expectTypeOf(todos).toEqualTypeOf() + + expectTypeOf(alerts).toEqualTypeOf() + + expectTypeOf(structuredSelector.argsMemoize).toEqualTypeOf< + typeof weakMapMemoize + >(weakMapMemoize) + + expectTypeOf(structuredSelector.memoize).toEqualTypeOf< + typeof weakMapMemoize + >(weakMapMemoize) + + expectTypeOf(structuredSelector.clearCache).returns.toBeVoid() + + expectTypeOf(structuredSelector.clearCache).parameters.toEqualTypeOf<[]>() + + expectTypeOf(structuredSelector.dependencies).items.toBeFunction() + + expectTypeOf(structuredSelector.dependencyRecomputations).toEqualTypeOf< + () => number + >() + + expectTypeOf(structuredSelector.recomputations).toEqualTypeOf< + () => number + >() + + expectTypeOf( + structuredSelector.resetDependencyRecomputations, + ).toEqualTypeOf<() => void>() + + expectTypeOf(structuredSelector.resetRecomputations).toEqualTypeOf< + () => void + >() + + expectTypeOf( + structuredSelector.lastResult, + ).returns.toEqualTypeOf(rootState) + + expectTypeOf( + structuredSelector.memoizedResultFunc, + ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ + rootState.todos, + rootState.alerts, + ]) + + expectTypeOf(structuredSelector.memoizedResultFunc).returns.toEqualTypeOf< + ReturnType + >(structuredSelector.lastResult()) + + expectTypeOf(structuredSelector.memoizedResultFunc).toHaveProperty( + 'clearCache', + ) + + expectTypeOf(structuredSelector.resultFunc).returns.toEqualTypeOf< + ReturnType + >(structuredSelector.lastResult()) + }) + + // TODO: Remove this test block once `TypedStructuredSelectorCreator` is removed. + test('TypedStructuredSelectorCreator should correctly infer memoize and argsMemoize', () => { + const createSelectorLru = createSelectorCreator({ + memoize: lruMemoize, + argsMemoize: microMemoize, + }) + + const createStructuredAppSelector: TypedStructuredSelectorCreator = + createStructuredSelector + + const structuredSelector = createStructuredAppSelector( + { + todos: state => state.todos, + alerts: state => state.alerts, + }, + createSelectorLru, + ) + + expectTypeOf(structuredSelector.argsMemoize).toEqualTypeOf< + typeof microMemoize + >(microMemoize) + + expectTypeOf(structuredSelector.memoize).toEqualTypeOf( + lruMemoize, + ) + + const { todos, alerts } = structuredSelector(rootState) + + expectTypeOf(todos).toEqualTypeOf() + + expectTypeOf(alerts).toEqualTypeOf() + + expectTypeOf(structuredSelector.dependencies).items.toBeFunction() + + expectTypeOf(structuredSelector.dependencyRecomputations).toEqualTypeOf< + () => number + >() + + expectTypeOf(structuredSelector.recomputations).toEqualTypeOf< + () => number + >() + + expectTypeOf( + structuredSelector.resetDependencyRecomputations, + ).toEqualTypeOf<() => void>() + + expectTypeOf(structuredSelector.resetRecomputations).toEqualTypeOf< + () => void + >() + + expectTypeOf( + structuredSelector.lastResult, + ).returns.toEqualTypeOf(rootState) + + expectTypeOf( + structuredSelector.memoizedResultFunc, + ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ + rootState.todos, + rootState.alerts, + ]) + + expectTypeOf(structuredSelector.memoizedResultFunc).returns.toEqualTypeOf< + ReturnType + >(structuredSelector.lastResult()) + + expectTypeOf(structuredSelector.memoizedResultFunc).toHaveProperty( + 'clearCache', + ) + + expectTypeOf(structuredSelector.resultFunc).returns.toEqualTypeOf< + ReturnType + >(structuredSelector.lastResult()) + }) + + test('supports additional parameters', () => { + const structuredSelector = createStructuredSelector({ + todos: (state: RootState) => state.todos, + alerts: (state: RootState) => state.alerts, + todoById: (state: RootState, id: number) => state.todos[id], + }) + + const { alerts, todos, todoById } = structuredSelector(rootState, 0) + + expectTypeOf(todos).toEqualTypeOf() + + expectTypeOf(alerts).toEqualTypeOf() + + expectTypeOf(todoById).toEqualTypeOf() + + expectTypeOf(structuredSelector.argsMemoize).toEqualTypeOf< + typeof weakMapMemoize + >(weakMapMemoize) + + expectTypeOf(structuredSelector.memoize).toEqualTypeOf< + typeof weakMapMemoize + >(weakMapMemoize) + + expectTypeOf(structuredSelector.clearCache).returns.toBeVoid() + + expectTypeOf(structuredSelector.clearCache).parameters.toEqualTypeOf<[]>() + + expectTypeOf(structuredSelector.dependencies).items.toMatchTypeOf< + Selector + >() + + expectTypeOf(structuredSelector.dependencyRecomputations).toEqualTypeOf< + () => number + >() + + expectTypeOf(structuredSelector.recomputations).toEqualTypeOf< + () => number + >() + + expectTypeOf( + structuredSelector.resetDependencyRecomputations, + ).returns.toBeVoid() + + expectTypeOf( + structuredSelector.resetDependencyRecomputations, + ).parameters.items.toBeNever() + + expectTypeOf(structuredSelector.resetRecomputations).returns.toBeVoid() + + expectTypeOf( + structuredSelector.resetRecomputations, + ).parameters.items.toBeNever() + + // Use `.branded` for intersection types https://github.com/mmkal/expect-type#why-is-my-assertion-failing + expectTypeOf(structuredSelector.lastResult).returns.branded.toEqualTypeOf< + RootState & { todoById: Todo } + >() + + expectTypeOf( + structuredSelector.memoizedResultFunc, + ).parameters.toEqualTypeOf<[Todo[], Alert[], Todo]>([ + rootState.todos, + rootState.alerts, + rootState.todos[0], + ]) + + expectTypeOf(structuredSelector.resultFunc).parameters.toEqualTypeOf< + [Todo[], Alert[], Todo] + >([rootState.todos, rootState.alerts, rootState.todos[0]]) + + expectTypeOf(structuredSelector.memoizedResultFunc).returns.toEqualTypeOf< + ReturnType + >(structuredSelector.lastResult()) + + expectTypeOf(structuredSelector.memoizedResultFunc).toHaveProperty( + 'clearCache', + ) + + expectTypeOf(structuredSelector.resultFunc).returns.toEqualTypeOf< + ReturnType + >(structuredSelector.lastResult()) + }) +}) diff --git a/type-tests/createStructuredSelector.withTypes.test-d.ts b/type-tests/createStructuredSelector.withTypes.test-d.ts index 9bb5b1ff7..3e9b3aae1 100644 --- a/type-tests/createStructuredSelector.withTypes.test-d.ts +++ b/type-tests/createStructuredSelector.withTypes.test-d.ts @@ -2,14 +2,14 @@ import microMemoize from 'micro-memoize' import type { Selector, StructuredSelectorCreator, - TypedStructuredSelectorCreator + TypedStructuredSelectorCreator, } from 'reselect' import { createSelector, createSelectorCreator, createStructuredSelector, lruMemoize, - weakMapMemoize + weakMapMemoize, } from 'reselect' import { describe, expectTypeOf, test } from 'vitest' @@ -31,12 +31,12 @@ interface RootState { const rootState: RootState = { todos: [ { id: 0, completed: false }, - { id: 1, completed: false } + { id: 1, completed: false }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: false } - ] + { id: 1, read: false }, + ], } describe('createStructuredSelector.withTypes()', () => { @@ -45,7 +45,7 @@ describe('createStructuredSelector.withTypes()', () => { test('locks down state type and infers types correctly', () => { expectTypeOf(createStructuredSelector.withTypes).returns.toEqualTypeOf( - createStructuredSelector + createStructuredSelector, ) const structuredAppSelector = createStructuredAppSelector({ @@ -58,7 +58,7 @@ describe('createStructuredSelector.withTypes()', () => { expectTypeOf(state).toEqualTypeOf(rootState) return state.alerts - } + }, }) const { todos, alerts } = structuredAppSelector(rootState) @@ -92,7 +92,7 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.resetDependencyRecomputations + structuredAppSelector.resetDependencyRecomputations, ).toEqualTypeOf<() => void>() expectTypeOf(structuredAppSelector.resetRecomputations).toEqualTypeOf< @@ -100,24 +100,24 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.lastResult + structuredAppSelector.lastResult, ).returns.toEqualTypeOf(rootState) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ rootState.todos, - rootState.alerts + rootState.alerts, ]) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).returns.toEqualTypeOf< ReturnType >(structuredAppSelector.lastResult()) expectTypeOf(structuredAppSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' + 'clearCache', ) expectTypeOf(structuredAppSelector.resultFunc).returns.toEqualTypeOf< @@ -128,15 +128,15 @@ describe('createStructuredSelector.withTypes()', () => { test('should correctly infer memoize and argsMemoize', () => { const createSelectorLru = createSelectorCreator({ memoize: lruMemoize, - argsMemoize: microMemoize + argsMemoize: microMemoize, }) const structuredSelector = createStructuredAppSelector( { todos: state => state.todos, - alerts: state => state.alerts + alerts: state => state.alerts, }, - createSelectorLru + createSelectorLru, ) expectTypeOf(structuredSelector.argsMemoize).toEqualTypeOf< @@ -144,7 +144,7 @@ describe('createStructuredSelector.withTypes()', () => { >(microMemoize) expectTypeOf(structuredSelector.memoize).toEqualTypeOf( - lruMemoize + lruMemoize, ) const { todos, alerts } = structuredSelector(rootState) @@ -164,7 +164,7 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredSelector.resetDependencyRecomputations + structuredSelector.resetDependencyRecomputations, ).toEqualTypeOf<() => void>() expectTypeOf(structuredSelector.resetRecomputations).toEqualTypeOf< @@ -172,14 +172,14 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredSelector.lastResult + structuredSelector.lastResult, ).returns.toEqualTypeOf(rootState) expectTypeOf( - structuredSelector.memoizedResultFunc + structuredSelector.memoizedResultFunc, ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ rootState.todos, - rootState.alerts + rootState.alerts, ]) expectTypeOf(structuredSelector.memoizedResultFunc).returns.toEqualTypeOf< @@ -187,7 +187,7 @@ describe('createStructuredSelector.withTypes()', () => { >(structuredSelector.lastResult()) expectTypeOf(structuredSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' + 'clearCache', ) expectTypeOf(structuredSelector.resultFunc).returns.toEqualTypeOf< @@ -211,7 +211,7 @@ describe('createStructuredSelector.withTypes()', () => { expectTypeOf(state).toEqualTypeOf(rootState) return state.todos[id] - } + }, }) const { alerts, todos, todoById } = structuredAppSelector(rootState, 0) @@ -249,30 +249,30 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.resetDependencyRecomputations + structuredAppSelector.resetDependencyRecomputations, ).returns.toBeVoid() expectTypeOf( - structuredAppSelector.resetDependencyRecomputations + structuredAppSelector.resetDependencyRecomputations, ).parameters.items.toBeNever() expectTypeOf(structuredAppSelector.resetRecomputations).returns.toBeVoid() expectTypeOf( - structuredAppSelector.resetRecomputations + structuredAppSelector.resetRecomputations, ).parameters.items.toBeNever() // Use `.branded` for intersection types https://github.com/mmkal/expect-type#why-is-my-assertion-failing expectTypeOf( - structuredAppSelector.lastResult + structuredAppSelector.lastResult, ).returns.branded.toEqualTypeOf() expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).parameters.toEqualTypeOf<[Todo[], Alert[], Todo]>([ rootState.todos, rootState.alerts, - rootState.todos[0] + rootState.todos[0], ]) expectTypeOf(structuredAppSelector.resultFunc).parameters.toEqualTypeOf< @@ -280,13 +280,13 @@ describe('createStructuredSelector.withTypes()', () => { >([rootState.todos, rootState.alerts, rootState.todos[0]]) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).returns.toEqualTypeOf< ReturnType >(structuredAppSelector.lastResult()) expectTypeOf(structuredAppSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' + 'clearCache', ) expectTypeOf(structuredAppSelector.resultFunc).returns.toEqualTypeOf< @@ -309,7 +309,7 @@ describe('createStructuredSelector.withTypes()', () => { expectTypeOf(state).toEqualTypeOf(rootState) return state.alerts - } + }, }) const { todos, alerts } = structuredAppSelector(rootState) @@ -343,7 +343,7 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.resetDependencyRecomputations + structuredAppSelector.resetDependencyRecomputations, ).toEqualTypeOf<() => void>() expectTypeOf(structuredAppSelector.resetRecomputations).toEqualTypeOf< @@ -351,24 +351,24 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.lastResult + structuredAppSelector.lastResult, ).returns.toEqualTypeOf(rootState) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ rootState.todos, - rootState.alerts + rootState.alerts, ]) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).returns.toEqualTypeOf< ReturnType >(structuredAppSelector.lastResult()) expectTypeOf(structuredAppSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' + 'clearCache', ) expectTypeOf(structuredAppSelector.resultFunc).returns.toEqualTypeOf< @@ -388,9 +388,9 @@ describe('createStructuredSelector.withTypes()', () => { expectTypeOf(state).toEqualTypeOf(rootState) return state.alerts - } + }, }, - createSelector.withTypes() + createSelector.withTypes(), ) const { todos, alerts } = structuredAppSelector(rootState) @@ -424,7 +424,7 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.resetDependencyRecomputations + structuredAppSelector.resetDependencyRecomputations, ).toEqualTypeOf<() => void>() expectTypeOf(structuredAppSelector.resetRecomputations).toEqualTypeOf< @@ -432,24 +432,24 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.lastResult + structuredAppSelector.lastResult, ).returns.toEqualTypeOf(rootState) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ rootState.todos, - rootState.alerts + rootState.alerts, ]) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).returns.toEqualTypeOf< ReturnType >(structuredAppSelector.lastResult()) expectTypeOf(structuredAppSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' + 'clearCache', ) expectTypeOf(structuredAppSelector.resultFunc).returns.toEqualTypeOf< @@ -472,9 +472,9 @@ describe('createStructuredSelector.withTypes()', () => { expectTypeOf(state).toEqualTypeOf(rootState) return state.alerts - } + }, }, - createSelector.withTypes() + createSelector.withTypes(), ) const { todos, alerts } = structuredAppSelector(rootState) @@ -508,7 +508,7 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.resetDependencyRecomputations + structuredAppSelector.resetDependencyRecomputations, ).toEqualTypeOf<() => void>() expectTypeOf(structuredAppSelector.resetRecomputations).toEqualTypeOf< @@ -516,24 +516,24 @@ describe('createStructuredSelector.withTypes()', () => { >() expectTypeOf( - structuredAppSelector.lastResult + structuredAppSelector.lastResult, ).returns.toEqualTypeOf(rootState) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).parameters.toEqualTypeOf<[Todo[], Alert[]]>([ rootState.todos, - rootState.alerts + rootState.alerts, ]) expectTypeOf( - structuredAppSelector.memoizedResultFunc + structuredAppSelector.memoizedResultFunc, ).returns.toEqualTypeOf< ReturnType >(structuredAppSelector.lastResult()) expectTypeOf(structuredAppSelector.memoizedResultFunc).toHaveProperty( - 'clearCache' + 'clearCache', ) expectTypeOf(structuredAppSelector.resultFunc).returns.toEqualTypeOf< diff --git a/type-tests/deepNesting.test-d.ts b/type-tests/deepNesting.test-d.ts index e602026f1..8504f0f44 100644 --- a/type-tests/deepNesting.test-d.ts +++ b/type-tests/deepNesting.test-d.ts @@ -1,320 +1,320 @@ -import microMemoize from 'micro-memoize' -import { createSelector, lruMemoize } from 'reselect' -import { describe, test } from 'vitest' - -interface RootState { - todos: { - id: number - completed: boolean - }[] -} - -const state: RootState = { - todos: [ - { id: 0, completed: false }, - { id: 1, completed: false } - ] -} - -describe('deep nesting', () => { - test('Deep Nesting First And Second createSelector Overload', () => { - type State = { foo: string } - const readOne = (state: State) => state.foo - - const selector0 = createSelector(readOne, one => one) - const selector1 = createSelector(selector0, s => s) - const selector2 = createSelector(selector1, s => s) - const selector3 = createSelector(selector2, s => s) - const selector4 = createSelector(selector3, s => s) - const selector5 = createSelector(selector4, s => s) - const selector6 = createSelector(selector5, s => s) - const selector7 = createSelector(selector6, s => s) - const selector8 = createSelector(selector7, s => s) - const selector9 = createSelector(selector8, s => s) - const selector10 = createSelector(selector9, s => s, { - memoize: microMemoize - }) - selector10.dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].memoizedResultFunc.clearCache - const selector11 = createSelector(selector10, s => s) - const selector12 = createSelector(selector11, s => s) - const selector13 = createSelector(selector12, s => s) - const selector14 = createSelector(selector13, s => s) - const selector15 = createSelector(selector14, s => s) - const selector16 = createSelector(selector15, s => s) - const selector17 = createSelector(selector16, s => s) - const selector18 = createSelector(selector17, s => s) - const selector19 = createSelector(selector18, s => s) - const selector20 = createSelector(selector19, s => s) - selector20.dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].memoizedResultFunc.cache - const selector21 = createSelector(selector20, s => s) - const selector22 = createSelector(selector21, s => s) - const selector23 = createSelector(selector22, s => s) - const selector24 = createSelector(selector23, s => s) - const selector25 = createSelector(selector24, s => s) - const selector26 = createSelector(selector25, s => s) - const selector27 = createSelector(selector26, s => s) - const selector28 = createSelector(selector27, s => s) - const selector29 = createSelector(selector28, s => s) - const selector30 = createSelector(selector29, s => s) - selector30.dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].memoizedResultFunc.clearCache - }) - test('Deep Nesting Second createSelector Overload', () => { - type State = { foo: string } - const readOne = (state: State) => state.foo - - const selector0 = createSelector(readOne, one => one) - const selector1 = createSelector(selector0, s => s, { - memoize: lruMemoize - }) - const selector2 = createSelector(selector1, s => s, { - memoize: lruMemoize - }) - const selector3 = createSelector(selector2, s => s, { - memoize: lruMemoize - }) - const selector4 = createSelector(selector3, s => s, { - memoize: lruMemoize - }) - const selector5 = createSelector(selector4, s => s, { - memoize: lruMemoize - }) - const selector6 = createSelector(selector5, s => s, { - memoize: lruMemoize - }) - const selector7 = createSelector(selector6, s => s, { - memoize: lruMemoize - }) - const selector8 = createSelector(selector7, s => s, { - memoize: lruMemoize - }) - const selector9 = createSelector(selector8, s => s, { - memoize: lruMemoize - }) - const selector10 = createSelector(selector9, s => s, { - memoize: lruMemoize - }) - const selector11 = createSelector(selector10, s => s, { - memoize: lruMemoize - }) - const selector12 = createSelector(selector11, s => s, { - memoize: lruMemoize - }) - const selector13 = createSelector(selector12, s => s, { - memoize: lruMemoize - }) - const selector14 = createSelector(selector13, s => s, { - memoize: lruMemoize - }) - const selector15 = createSelector(selector14, s => s, { - memoize: lruMemoize - }) - const selector16 = createSelector(selector15, s => s, { - memoize: lruMemoize - }) - const selector17 = createSelector(selector16, s => s, { - memoize: lruMemoize - }) - const selector18 = createSelector(selector17, s => s, { - memoize: lruMemoize - }) - const selector19 = createSelector(selector18, s => s, { - memoize: lruMemoize - }) - const selector20 = createSelector(selector19, s => s, { - memoize: lruMemoize - }) - const selector21 = createSelector(selector20, s => s, { - memoize: lruMemoize - }) - const selector22 = createSelector(selector21, s => s, { - memoize: lruMemoize - }) - const selector23 = createSelector(selector22, s => s, { - memoize: lruMemoize - }) - const selector24 = createSelector(selector23, s => s, { - memoize: lruMemoize - }) - const selector25 = createSelector(selector24, s => s, { - memoize: lruMemoize - }) - const selector26 = createSelector(selector25, s => s, { - memoize: lruMemoize - }) - const selector27 = createSelector(selector26, s => s, { - memoize: lruMemoize - }) - const selector28 = createSelector(selector27, s => s, { - memoize: lruMemoize - }) - const selector29 = createSelector(selector28, s => s, { - memoize: lruMemoize - }) - }) - - test('Deep Nesting Third createSelector Overload', () => { - type State = { foo: string } - const readOne = (state: State) => state.foo - - const selector0 = createSelector(readOne, one => one) - const selector1 = createSelector([selector0], s => s) - const selector2 = createSelector([selector1], s => s) - const selector3 = createSelector([selector2], s => s) - const selector4 = createSelector([selector3], s => s) - const selector5 = createSelector([selector4], s => s) - const selector6 = createSelector([selector5], s => s) - const selector7 = createSelector([selector6], s => s) - const selector8 = createSelector([selector7], s => s) - const selector9 = createSelector([selector8], s => s) - const selector10 = createSelector([selector9], s => s) - const selector11 = createSelector([selector10], s => s) - const selector12 = createSelector([selector11], s => s) - const selector13 = createSelector([selector12], s => s) - const selector14 = createSelector([selector13], s => s) - const selector15 = createSelector([selector14], s => s) - const selector16 = createSelector([selector15], s => s) - const selector17 = createSelector([selector16], s => s) - const selector18 = createSelector([selector17], s => s) - const selector19 = createSelector([selector18], s => s) - const selector20 = createSelector([selector19], s => s) - const selector21 = createSelector([selector20], s => s) - const selector22 = createSelector([selector21], s => s) - const selector23 = createSelector([selector22], s => s) - const selector24 = createSelector([selector23], s => s) - const selector25 = createSelector([selector24], s => s) - const selector26 = createSelector([selector25], s => s) - const selector27 = createSelector([selector26], s => s) - const selector28 = createSelector([selector27], s => s) - const selector29 = createSelector([selector28], s => s) - const selector30 = createSelector([selector29], s => s) - }) - - test('createSelector Parameter Limit', () => { - const selector = createSelector( - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testBoolean: boolean }) => state.testBoolean, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testBoolean: boolean }) => state.testBoolean, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testBoolean: boolean }) => state.testBoolean, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testBoolean: boolean }) => state.testBoolean, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray, - ( - foo1: string, - foo2: number, - foo3: boolean, - foo4: string, - foo5: string, - foo6: string, - foo7: string, - foo8: number, - foo9: string[], - foo10: string, - foo11: number, - foo12: boolean, - foo13: string, - foo14: string, - foo15: string, - foo16: string, - foo17: number, - foo18: string[], - foo19: string, - foo20: number, - foo21: boolean, - foo22: string, - foo23: string, - foo24: string, - foo25: string, - foo26: number, - foo27: string[], - foo28: string, - foo29: number, - foo30: boolean, - foo31: string, - foo32: string, - foo33: string, - foo34: string, - foo35: number, - foo36: string[] - ) => { - return { - foo1, - foo2, - foo3, - foo4, - foo5, - foo6, - foo7, - foo8, - foo9, - foo10, - foo11, - foo12, - foo13, - foo14, - foo15, - foo16, - foo17, - foo18, - foo19, - foo20, - foo21, - foo22, - foo23, - foo24, - foo25, - foo26, - foo27, - foo28, - foo29, - foo30, - foo31, - foo32, - foo33, - foo34, - foo35, - foo36 - } - } - ) - }) -}) +import microMemoize from 'micro-memoize' +import { createSelector, lruMemoize } from 'reselect' +import { describe, test } from 'vitest' + +interface RootState { + todos: { + id: number + completed: boolean + }[] +} + +const state: RootState = { + todos: [ + { id: 0, completed: false }, + { id: 1, completed: false }, + ], +} + +describe('deep nesting', () => { + test('Deep Nesting First And Second createSelector Overload', () => { + type State = { foo: string } + const readOne = (state: State) => state.foo + + const selector0 = createSelector(readOne, one => one) + const selector1 = createSelector(selector0, s => s) + const selector2 = createSelector(selector1, s => s) + const selector3 = createSelector(selector2, s => s) + const selector4 = createSelector(selector3, s => s) + const selector5 = createSelector(selector4, s => s) + const selector6 = createSelector(selector5, s => s) + const selector7 = createSelector(selector6, s => s) + const selector8 = createSelector(selector7, s => s) + const selector9 = createSelector(selector8, s => s) + const selector10 = createSelector(selector9, s => s, { + memoize: microMemoize, + }) + selector10.dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].memoizedResultFunc.clearCache + const selector11 = createSelector(selector10, s => s) + const selector12 = createSelector(selector11, s => s) + const selector13 = createSelector(selector12, s => s) + const selector14 = createSelector(selector13, s => s) + const selector15 = createSelector(selector14, s => s) + const selector16 = createSelector(selector15, s => s) + const selector17 = createSelector(selector16, s => s) + const selector18 = createSelector(selector17, s => s) + const selector19 = createSelector(selector18, s => s) + const selector20 = createSelector(selector19, s => s) + selector20.dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].memoizedResultFunc.cache + const selector21 = createSelector(selector20, s => s) + const selector22 = createSelector(selector21, s => s) + const selector23 = createSelector(selector22, s => s) + const selector24 = createSelector(selector23, s => s) + const selector25 = createSelector(selector24, s => s) + const selector26 = createSelector(selector25, s => s) + const selector27 = createSelector(selector26, s => s) + const selector28 = createSelector(selector27, s => s) + const selector29 = createSelector(selector28, s => s) + const selector30 = createSelector(selector29, s => s) + selector30.dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].memoizedResultFunc.clearCache + }) + test('Deep Nesting Second createSelector Overload', () => { + type State = { foo: string } + const readOne = (state: State) => state.foo + + const selector0 = createSelector(readOne, one => one) + const selector1 = createSelector(selector0, s => s, { + memoize: lruMemoize, + }) + const selector2 = createSelector(selector1, s => s, { + memoize: lruMemoize, + }) + const selector3 = createSelector(selector2, s => s, { + memoize: lruMemoize, + }) + const selector4 = createSelector(selector3, s => s, { + memoize: lruMemoize, + }) + const selector5 = createSelector(selector4, s => s, { + memoize: lruMemoize, + }) + const selector6 = createSelector(selector5, s => s, { + memoize: lruMemoize, + }) + const selector7 = createSelector(selector6, s => s, { + memoize: lruMemoize, + }) + const selector8 = createSelector(selector7, s => s, { + memoize: lruMemoize, + }) + const selector9 = createSelector(selector8, s => s, { + memoize: lruMemoize, + }) + const selector10 = createSelector(selector9, s => s, { + memoize: lruMemoize, + }) + const selector11 = createSelector(selector10, s => s, { + memoize: lruMemoize, + }) + const selector12 = createSelector(selector11, s => s, { + memoize: lruMemoize, + }) + const selector13 = createSelector(selector12, s => s, { + memoize: lruMemoize, + }) + const selector14 = createSelector(selector13, s => s, { + memoize: lruMemoize, + }) + const selector15 = createSelector(selector14, s => s, { + memoize: lruMemoize, + }) + const selector16 = createSelector(selector15, s => s, { + memoize: lruMemoize, + }) + const selector17 = createSelector(selector16, s => s, { + memoize: lruMemoize, + }) + const selector18 = createSelector(selector17, s => s, { + memoize: lruMemoize, + }) + const selector19 = createSelector(selector18, s => s, { + memoize: lruMemoize, + }) + const selector20 = createSelector(selector19, s => s, { + memoize: lruMemoize, + }) + const selector21 = createSelector(selector20, s => s, { + memoize: lruMemoize, + }) + const selector22 = createSelector(selector21, s => s, { + memoize: lruMemoize, + }) + const selector23 = createSelector(selector22, s => s, { + memoize: lruMemoize, + }) + const selector24 = createSelector(selector23, s => s, { + memoize: lruMemoize, + }) + const selector25 = createSelector(selector24, s => s, { + memoize: lruMemoize, + }) + const selector26 = createSelector(selector25, s => s, { + memoize: lruMemoize, + }) + const selector27 = createSelector(selector26, s => s, { + memoize: lruMemoize, + }) + const selector28 = createSelector(selector27, s => s, { + memoize: lruMemoize, + }) + const selector29 = createSelector(selector28, s => s, { + memoize: lruMemoize, + }) + }) + + test('Deep Nesting Third createSelector Overload', () => { + type State = { foo: string } + const readOne = (state: State) => state.foo + + const selector0 = createSelector(readOne, one => one) + const selector1 = createSelector([selector0], s => s) + const selector2 = createSelector([selector1], s => s) + const selector3 = createSelector([selector2], s => s) + const selector4 = createSelector([selector3], s => s) + const selector5 = createSelector([selector4], s => s) + const selector6 = createSelector([selector5], s => s) + const selector7 = createSelector([selector6], s => s) + const selector8 = createSelector([selector7], s => s) + const selector9 = createSelector([selector8], s => s) + const selector10 = createSelector([selector9], s => s) + const selector11 = createSelector([selector10], s => s) + const selector12 = createSelector([selector11], s => s) + const selector13 = createSelector([selector12], s => s) + const selector14 = createSelector([selector13], s => s) + const selector15 = createSelector([selector14], s => s) + const selector16 = createSelector([selector15], s => s) + const selector17 = createSelector([selector16], s => s) + const selector18 = createSelector([selector17], s => s) + const selector19 = createSelector([selector18], s => s) + const selector20 = createSelector([selector19], s => s) + const selector21 = createSelector([selector20], s => s) + const selector22 = createSelector([selector21], s => s) + const selector23 = createSelector([selector22], s => s) + const selector24 = createSelector([selector23], s => s) + const selector25 = createSelector([selector24], s => s) + const selector26 = createSelector([selector25], s => s) + const selector27 = createSelector([selector26], s => s) + const selector28 = createSelector([selector27], s => s) + const selector29 = createSelector([selector28], s => s) + const selector30 = createSelector([selector29], s => s) + }) + + test('createSelector Parameter Limit', () => { + const selector = createSelector( + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testBoolean: boolean }) => state.testBoolean, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testStringArray: string[] }) => state.testStringArray, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testBoolean: boolean }) => state.testBoolean, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testStringArray: string[] }) => state.testStringArray, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testBoolean: boolean }) => state.testBoolean, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testStringArray: string[] }) => state.testStringArray, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testBoolean: boolean }) => state.testBoolean, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testStringArray: string[] }) => state.testStringArray, + ( + foo1: string, + foo2: number, + foo3: boolean, + foo4: string, + foo5: string, + foo6: string, + foo7: string, + foo8: number, + foo9: string[], + foo10: string, + foo11: number, + foo12: boolean, + foo13: string, + foo14: string, + foo15: string, + foo16: string, + foo17: number, + foo18: string[], + foo19: string, + foo20: number, + foo21: boolean, + foo22: string, + foo23: string, + foo24: string, + foo25: string, + foo26: number, + foo27: string[], + foo28: string, + foo29: number, + foo30: boolean, + foo31: string, + foo32: string, + foo33: string, + foo34: string, + foo35: number, + foo36: string[], + ) => { + return { + foo1, + foo2, + foo3, + foo4, + foo5, + foo6, + foo7, + foo8, + foo9, + foo10, + foo11, + foo12, + foo13, + foo14, + foo15, + foo16, + foo17, + foo18, + foo19, + foo20, + foo21, + foo22, + foo23, + foo24, + foo25, + foo26, + foo27, + foo28, + foo29, + foo30, + foo31, + foo32, + foo33, + foo34, + foo35, + foo36, + } + }, + ) + }) +}) diff --git a/type-tests/tsconfig.json b/type-tests/tsconfig.json index 4fae7b4d6..0cf05eb4d 100644 --- a/type-tests/tsconfig.json +++ b/type-tests/tsconfig.json @@ -1,17 +1,17 @@ -{ - "compilerOptions": { - "module": "commonjs", - "esModuleInterop": true, - "strict": true, - "target": "ES2015", - "lib": ["ES2021.WeakRef"], - "declaration": true, - "noEmit": true, - "skipLibCheck": true, - "paths": { - "reselect": ["../src/index"], // @remap-prod-remove-line - "@internal/*": ["../src/*"] - } - }, - "include": ["**/*.ts", "../typescript_test/**/*.ts"] -} +{ + "compilerOptions": { + "module": "commonjs", + "esModuleInterop": true, + "strict": true, + "target": "ES2015", + "lib": ["ES2021.WeakRef"], + "declaration": true, + "noEmit": true, + "skipLibCheck": true, + "paths": { + "reselect": ["../src/index"], // @remap-prod-remove-line + "@internal/*": ["../src/*"] + } + }, + "include": ["**/*.ts", "../typescript_test/**/*.ts"] +} diff --git a/typescript_test/argsMemoize.typetest.ts b/typescript_test/argsMemoize.typetest.ts index 1e883ab99..9204e7a91 100644 --- a/typescript_test/argsMemoize.typetest.ts +++ b/typescript_test/argsMemoize.typetest.ts @@ -1,1160 +1,1162 @@ -import memoizeOne from 'memoize-one' -import microMemoize from 'micro-memoize' -import { - unstable_autotrackMemoize as autotrackMemoize, - createSelector, - createSelectorCreator, - lruMemoize, - weakMapMemoize -} from 'reselect' -import { expectExactType } from './typesTestUtils' - -interface RootState { - todos: { - id: number - completed: boolean - }[] -} -const state: RootState = { - todos: [ - { id: 0, completed: false }, - { id: 1, completed: false } - ] -} - -function overrideOnlyMemoizeInCreateSelector() { - const selectorDefaultSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize } - ) - const selectorDefaultArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: lruMemoize } - ) - const selectorDefaultArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } } - ) - const selectorDefaultSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } } - ) - const selectorAutotrackSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: autotrackMemoize } - ) - const selectorAutotrackArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: autotrackMemoize } - ) - // @ts-expect-error When memoize is autotrackMemoize, type of memoizeOptions needs to be the same as options args in autotrackMemoize. - const selectorAutotrackArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: autotrackMemoize, memoizeOptions: { maxSize: 2 } } - ) - // @ts-expect-error When memoize is autotrackMemoize, type of memoizeOptions needs to be the same as options args in autotrackMemoize. - const selectorAutotrackSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: autotrackMemoize, memoizeOptions: { maxSize: 2 } } - ) - const selectorWeakMapSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: weakMapMemoize } - ) - const selectorWeakMapArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { memoize: weakMapMemoize } - ) - // @ts-expect-error When memoize is weakMapMemoize, type of memoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } } - ) - // @ts-expect-error When memoize is weakMapMemoize, type of memoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } } - ) - const createSelectorDefault = createSelectorCreator(lruMemoize) - const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) - const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) - const changeMemoizeMethodSelectorDefault = createSelectorDefault( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: weakMapMemoize } - ) - const changeMemoizeMethodSelectorWeakMap = createSelectorWeakMap( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize } - ) - const changeMemoizeMethodSelectorAutotrack = createSelectorAutotrack( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize } - ) - const changeMemoizeMethodSelectorDefaultWithMemoizeOptions = - // @ts-expect-error When memoize is changed to weakMapMemoize or autotrackMemoize, memoizeOptions cannot be the same type as options args in lruMemoize. - createSelectorDefault( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } } - ) - const changeMemoizeMethodSelectorWeakMapWithMemoizeOptions = - createSelectorWeakMap( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } } // When memoize is changed to lruMemoize, memoizeOptions can now be the same type as options args in lruMemoize. - ) - const changeMemoizeMethodSelectorAutotrackWithMemoizeOptions = - createSelectorAutotrack( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } } // When memoize is changed to lruMemoize, memoizeOptions can now be the same type as options args in lruMemoize. - ) -} - -function overrideOnlyArgsMemoizeInCreateSelector() { - const selectorDefaultSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize } - ) - const selectorDefaultArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize } - ) - const selectorDefaultArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - const selectorDefaultSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - const selectorAutotrackSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: autotrackMemoize } - ) - const selectorAutotrackArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { argsMemoize: autotrackMemoize } - ) - // @ts-expect-error When argsMemoize is autotrackMemoize, type of argsMemoizeOptions needs to be the same as options args in autotrackMemoize. - const selectorAutotrackArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { - argsMemoize: autotrackMemoize, - argsMemoizeOptions: { maxSize: 2 } - } - ) - // @ts-expect-error When argsMemoize is autotrackMemoize, type of argsMemoizeOptions needs to be the same as options args in autotrackMemoize. - const selectorAutotrackSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - argsMemoize: autotrackMemoize, - argsMemoizeOptions: { maxSize: 2 } - } - ) - const selectorWeakMapSeparateInlineArgs = createSelector( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize } - ) - const selectorWeakMapArgsAsArray = createSelector( - [(state: RootState) => state.todos], - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize } - ) - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapArgsAsArrayWithMemoizeOptions = createSelector( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions = createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions1 = createSelector( - [ - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id) - ], - { - argsMemoize: weakMapMemoize, - argsMemoizeOptions: { maxSize: 2 } - } - ) - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions2 = createSelector( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: weakMapMemoize, - memoizeOptions: { - equalityCheck: - // @ts-expect-error - (a, b) => a === b, - maxSize: 2 - }, - argsMemoizeOptions: { maxSize: 2 } - } - ) - - const createSelectorLruMemoize = createSelectorCreator({ - memoize: lruMemoize - }) - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions3 = - // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. - createSelectorLruMemoize( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: weakMapMemoize, - // memoizeOptions: [], - memoizeOptions: [ - { - equalityCheck: - // @ts-expect-error - (a, b) => a === b, - maxSize: 2 - } - ], - argsMemoizeOptions: [{ maxSize: 2 }] - } - ) - - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions5 = - // @ts-expect-error - createSelectorLruMemoize( - [(state: RootState) => state.todos], - // @ts-expect-error - todos => todos.map(t => t.id), - { - argsMemoize: weakMapMemoize, - memoizeOptions: [{ isPromise: false }], - argsMemoizeOptions: [] - } - ) - const selectorWeakMapSeparateInlineArgsWithMemoizeOptions6 = - createSelectorLruMemoize( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { - argsMemoize: weakMapMemoize, - memoize: weakMapMemoize, - memoizeOptions: [], - argsMemoizeOptions: [] - // argsMemoizeOptions: (a, b) => a === b - } - ) - const createSelectorDefault = createSelectorCreator(lruMemoize) - const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) - const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) - const changeMemoizeMethodSelectorDefault = createSelectorDefault( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize } - ) - const changeMemoizeMethodSelectorWeakMap = createSelectorWeakMap( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize } - ) - const changeMemoizeMethodSelectorAutotrack = createSelectorAutotrack( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize } - ) - const changeMemoizeMethodSelectorDefaultWithMemoizeOptions = - // @ts-expect-error When argsMemoize is changed to weakMapMemoize or autotrackMemoize, argsMemoizeOptions cannot be the same type as options args in lruMemoize. - createSelectorDefault( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } } - ) - const changeMemoizeMethodSelectorWeakMapWithMemoizeOptions = - createSelectorWeakMap( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } } // When argsMemoize is changed to lruMemoize, argsMemoizeOptions can now be the same type as options args in lruMemoize. - ) - const changeMemoizeMethodSelectorAutotrackWithMemoizeOptions = - createSelectorAutotrack( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } } // When argsMemoize is changed to lruMemoize, argsMemoizeOptions can now be the same type as options args in lruMemoize. - ) -} - -function overrideMemoizeAndArgsMemoizeInCreateSelector() { - const createSelectorMicroMemoize = createSelectorCreator({ - memoize: microMemoize, - memoizeOptions: [{ isEqual: (a, b) => a === b }], - // memoizeOptions: { isEqual: (a, b) => a === b }, - argsMemoize: microMemoize, - argsMemoizeOptions: { isEqual: (a, b) => a === b } - }) - const selectorMicroMemoize = createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(({ id }) => id) - ) - expectExactType(selectorMicroMemoize(state)) - // @ts-expect-error - selectorMicroMemoize() - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoize.cache - selectorMicroMemoize.fn() - selectorMicroMemoize.isMemoized - selectorMicroMemoize.options - // @ts-expect-error - selectorMicroMemoize.clearCache() - // Checking existence of fields related to `memoize` - selectorMicroMemoize.memoizedResultFunc.cache - selectorMicroMemoize.memoizedResultFunc.fn() - selectorMicroMemoize.memoizedResultFunc.isMemoized - selectorMicroMemoize.memoizedResultFunc.options - // @ts-expect-error - selectorMicroMemoize.memoizedResultFunc.clearCache() - // Checking existence of fields related to the actual memoized selector - selectorMicroMemoize.dependencies - expectExactType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoize.dependencies) - expectExactType(selectorMicroMemoize.lastResult()) - // @ts-expect-error - selectorMicroMemoize.memoizedResultFunc() - expectExactType( - selectorMicroMemoize.memoizedResultFunc([{ id: 0, completed: true }]) - ) - selectorMicroMemoize.recomputations() - selectorMicroMemoize.resetRecomputations() - // @ts-expect-error - selectorMicroMemoize.resultFunc() - expectExactType( - selectorMicroMemoize.resultFunc([{ id: 0, completed: true }]) - ) - - // Checking to see if types dynamically change if memoize or argsMemoize are overridden inside `createSelector`. - // `microMemoize` was initially passed into `createSelectorCreator` - // as `memoize` and `argsMemoize`, After overriding them both to `lruMemoize`, - // not only does the type for `memoizeOptions` and `argsMemoizeOptions` change to - // the options parameter of `lruMemoize`, the output selector fields - // also change their type to the return type of `lruMemoize`. - const selectorMicroMemoizeOverridden = createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 2 }, - argsMemoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 3 } - } - ) - expectExactType(selectorMicroMemoizeOverridden(state)) - // @ts-expect-error - selectorMicroMemoizeOverridden() - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoizeOverridden.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.options - // Checking existence of fields related to `memoize` - selectorMicroMemoizeOverridden.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.memoizedResultFunc.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.memoizedResultFunc.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.memoizedResultFunc.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverridden.memoizedResultFunc.options - // Checking existence of fields related to the actual memoized selector - expectExactType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeOverridden.dependencies) - expectExactType( - selectorMicroMemoizeOverridden.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeOverridden.memoizedResultFunc() - selectorMicroMemoizeOverridden.recomputations() - selectorMicroMemoizeOverridden.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeOverridden.resultFunc() - expectExactType( - selectorMicroMemoizeOverridden.resultFunc([{ id: 0, completed: true }]) - ) - // Making sure the type behavior is consistent when args are passed in as an array. - const selectorMicroMemoizeOverriddenArray = createSelectorMicroMemoize( - [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 2 }, - argsMemoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 3 } - } - ) - expectExactType(selectorMicroMemoizeOverriddenArray(state)) - // @ts-expect-error - selectorMicroMemoizeOverriddenArray() - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoizeOverriddenArray.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.options - // Checking existence of fields related to `memoize` - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverriddenArray.memoizedResultFunc.options - // Checking existence of fields related to the actual memoized selector - expectExactType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeOverriddenArray.dependencies) - expectExactType( - selectorMicroMemoizeOverriddenArray.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeOverriddenArray.memoizedResultFunc() - selectorMicroMemoizeOverriddenArray.recomputations() - selectorMicroMemoizeOverriddenArray.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeOverriddenArray.resultFunc() - expectExactType( - selectorMicroMemoizeOverriddenArray.resultFunc([{ id: 0, completed: true }]) - ) - const selectorMicroMemoizeOverrideArgsMemoizeOnlyWrong = - // @ts-expect-error Because `memoizeOptions` should not contain `resultEqualityCheck`. - createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(({ id }) => id), - { - argsMemoize: lruMemoize, - memoizeOptions: { - isPromise: false, - resultEqualityCheck: - // @ts-expect-error - (a, b) => a === b - }, - argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b } - } - ) - const selectorMicroMemoizeOverrideArgsMemoizeOnly = - createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(({ id }) => id), - { - argsMemoize: lruMemoize, - memoizeOptions: { isPromise: false }, - argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b } - } - ) - expectExactType(selectorMicroMemoizeOverrideArgsMemoizeOnly(state)) - // @ts-expect-error - selectorMicroMemoizeOverrideArgsMemoizeOnly() - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoizeOverrideArgsMemoizeOnly.clearCache() // Prior to override, this field did NOT exist. - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideArgsMemoizeOnly.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideArgsMemoizeOnly.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideArgsMemoizeOnly.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideArgsMemoizeOnly.options - - // Checking existence of fields related to `memoize`, these should still be the same. - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.cache - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.fn() - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.isMemoized - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.options - // @ts-expect-error Note that since we did not override `memoize` in the options object, - // `memoizedResultFunc.clearCache` is still an invalid field access. - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.clearCache() - // Checking existence of fields related to the actual memoized selector - expectExactType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeOverrideArgsMemoizeOnly.dependencies) - expectExactType( - selectorMicroMemoizeOverrideArgsMemoizeOnly.lastResult() - ) - expectExactType( - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc() - selectorMicroMemoizeOverrideArgsMemoizeOnly.recomputations() - selectorMicroMemoizeOverrideArgsMemoizeOnly.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc() - expectExactType( - selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc([ - { id: 0, completed: true } - ]) - ) - - const selectorMicroMemoizeOverrideMemoizeOnly = createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - memoizeOptions: { resultEqualityCheck: (a, b) => a === b } - } - ) - expectExactType(selectorMicroMemoizeOverrideMemoizeOnly(state)) - // @ts-expect-error - selectorMicroMemoizeOverrideMemoizeOnly() - - // Checking existence of fields related to `argsMemoize` - selectorMicroMemoizeOverrideMemoizeOnly.cache - selectorMicroMemoizeOverrideMemoizeOnly.fn - selectorMicroMemoizeOverrideMemoizeOnly.isMemoized - selectorMicroMemoizeOverrideMemoizeOnly.options - // @ts-expect-error Note that since we did not override `argsMemoize` in the options object, - // `selector.clearCache` is still an invalid field access. - selectorMicroMemoizeOverrideMemoizeOnly.clearCache() - - // Checking existence of fields related to `memoize` - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.cache - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.fn() - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.isMemoized - // @ts-expect-error Prior to override, this field DID exist. - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.options - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. - - // Checking existence of fields related to the actual memoized selector - expectExactType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeOverrideMemoizeOnly.dependencies) - expectExactType( - selectorMicroMemoizeOverrideMemoizeOnly.lastResult() - ) - expectExactType( - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc() - selectorMicroMemoizeOverrideMemoizeOnly.recomputations() - selectorMicroMemoizeOverrideMemoizeOnly.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeOverrideMemoizeOnly.resultFunc() - expectExactType( - selectorMicroMemoizeOverrideMemoizeOnly.resultFunc([ - { id: 0, completed: true } - ]) - ) - - const selectorMicroMemoizePartiallyOverridden = - // @ts-expect-error Since `argsMemoize` is set to `lruMemoize`, `argsMemoizeOptions` must match the options object parameter of `lruMemoize` - createSelectorMicroMemoize( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: { - equalityCheck: - // @ts-expect-error - (a, b) => a === b, - maxSize: 2 - }, - argsMemoizeOptions: { isPromise: false } // This field causes a type error since it does not match the options param of `lruMemoize`. - } - ) - const selectorMicroMemoizePartiallyOverridden1 = - // @ts-expect-error Since `argsMemoize` is set to `lruMemoize`, `argsMemoizeOptions` must match the options object parameter of `lruMemoize` - createSelectorMicroMemoize( - (state: RootState) => state.todos, - // @ts-expect-error - todos => todos.map(t => t.id), - { - memoize: lruMemoize, - argsMemoize: lruMemoize, - memoizeOptions: [ - { - equalityCheck: - // @ts-expect-error - (a, b) => a === b, - maxSize: 2 - } - ], - argsMemoizeOptions: [{ isPromise: false }] // This field causes a type error since it does not match the options param of `lruMemoize`. - } - ) - const selectorMicroMemoizePartiallyOverridden2 = createSelectorMicroMemoize( - (state: RootState) => state.todos, - todos => todos.map(t => t.id), - { - argsMemoizeOptions: [{ isPromise: false }] - } - ) - - const selectorDefaultParametric = createSelector( - (state: RootState, id: number) => id, - (state: RootState) => state.todos, - (id, todos) => todos.filter(todo => todo.id === id), - { - argsMemoize: microMemoize, - devModeChecks: { inputStabilityCheck: 'never' }, - memoize: memoizeOne, - argsMemoizeOptions: [], - memoizeOptions: [(a, b) => a === b] - } - ) - expectExactType< - { - id: number - completed: boolean - }[] - >(selectorDefaultParametric(state, 0)) - expectExactType< - { - id: number - completed: boolean - }[] - >(selectorDefaultParametric(state, 1)) - // @ts-expect-error - selectorDefaultParametric(state) - // @ts-expect-error - selectorDefaultParametric(1) - // @ts-expect-error - selectorDefaultParametric(state, '') - // @ts-expect-error - selectorDefaultParametric(state, 1, 1) - // Checking existence of fields related to `argsMemoize` - // Prior to override, this field did NOT exist. - selectorDefaultParametric.cache - // Prior to override, this field did NOT exist. - selectorDefaultParametric.fn - // Prior to override, this field did NOT exist. - selectorDefaultParametric.isMemoized - // Prior to override, this field did NOT exist. - selectorDefaultParametric.options - // @ts-expect-error Prior to override, this field DID exist. - selectorDefaultParametric.clearCache() - - // Checking existence of fields related to `memoize` - // @ts-expect-error Prior to override, this field DID exist. - selectorDefaultParametric.memoizedResultFunc.clearCache() - // Prior to override, this field did NOT exist. - selectorDefaultParametric.memoizedResultFunc.clear() - - // Checking existence of fields related to the actual memoized selector - expectExactType< - [ - (state: RootState, id: number) => number, - (state: RootState) => { id: number; completed: boolean }[] - ] - >(selectorDefaultParametric.dependencies) - expectExactType<{ id: number; completed: boolean }[]>( - selectorDefaultParametric.lastResult() - ) - expectExactType<{ id: number; completed: boolean }[]>( - selectorDefaultParametric.memoizedResultFunc(0, [ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorDefaultParametric.memoizedResultFunc() - selectorDefaultParametric.recomputations() - selectorDefaultParametric.resetRecomputations() - // @ts-expect-error - selectorDefaultParametric.resultFunc() - expectExactType<{ id: number; completed: boolean }[]>( - selectorDefaultParametric.resultFunc(0, [{ id: 0, completed: true }]) - ) -} - -function memoizeAndArgsMemoizeInCreateSelectorCreator() { - // If we don't pass in `argsMemoize`, the type for `argsMemoizeOptions` - // falls back to the options parameter of `lruMemoize`. - const createSelectorArgsMemoizeOptionsFallbackToDefault = - createSelectorCreator({ - memoize: microMemoize, - memoizeOptions: [{ isPromise: false }], - argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b } - }) - const selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault = - createSelectorArgsMemoizeOptionsFallbackToDefault( - (state: RootState) => state.todos, - todos => todos.map(({ id }) => id) - ) - expectExactType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault(state) - ) - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault() - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.clearCache() - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.cache - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.fn - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.isMemoized - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.options - // Checking existence of fields related to `memoize` - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc - .cache - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc.fn() - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc - .isMemoized - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc - .options - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc.clearCache() - // Checking existence of fields related to the actual memoized selector - expectExactType< - [ - (state: RootState) => { - id: number - completed: boolean - }[] - ] - >(selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.dependencies) - expectExactType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.lastResult() - ) - expectExactType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc([ - { id: 0, completed: true } - ]) - ) - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc() - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.recomputations() - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resetRecomputations() - // @ts-expect-error - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc() - expectExactType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc([ - { id: 0, completed: true } - ]) - ) - expectExactType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoize - ) - expectExactType( - selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.argsMemoize - ) - - const createSelectorWithWrongArgsMemoizeOptions = - // @ts-expect-error If we don't pass in `argsMemoize`, the type for `argsMemoizeOptions` falls back to the options parameter of `weakMapMemoize`. - createSelectorCreator({ - memoize: microMemoize, - memoizeOptions: { isEqual: (a, b) => a === b }, - argsMemoizeOptions: { - isEqual: - // @ts-expect-error implicit any - (a, b) => a === b - } - }) - - // When passing in an options object as the first argument, there should be no other arguments. - const createSelectorWrong = createSelectorCreator( - { - // @ts-expect-error - memoize: microMemoize, - // @ts-expect-error - memoizeOptions: { isEqual: (a, b) => a === b }, - // @ts-expect-error - argsMemoizeOptions: { equalityCheck: (a, b) => a === b } - }, - [] // This causes the error. - ) -} - -function deepNesting() { - type State = { foo: string } - const readOne = (state: State) => state.foo - - const selector0 = createSelector(readOne, one => one) - const selector1 = createSelector(selector0, s => s) - const selector2 = createSelector(selector1, s => s) - const selector3 = createSelector(selector2, s => s) - const selector4 = createSelector(selector3, s => s) - const selector5 = createSelector(selector4, s => s) - const selector6 = createSelector(selector5, s => s) - const selector7 = createSelector(selector6, s => s) - const selector8 = createSelector(selector7, s => s) - const selector9 = createSelector(selector8, s => s) - const selector10 = createSelector(selector9, s => s, { - memoize: microMemoize - }) - selector10.dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].memoizedResultFunc.clearCache - const selector11 = createSelector(selector10, s => s) - const selector12 = createSelector(selector11, s => s) - const selector13 = createSelector(selector12, s => s) - const selector14 = createSelector(selector13, s => s) - const selector15 = createSelector(selector14, s => s) - const selector16 = createSelector(selector15, s => s) - const selector17 = createSelector(selector16, s => s) - const selector18 = createSelector(selector17, s => s) - const selector19 = createSelector(selector18, s => s) - const selector20 = createSelector(selector19, s => s) - selector20.dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].memoizedResultFunc.cache - const selector21 = createSelector(selector20, s => s) - const selector22 = createSelector(selector21, s => s) - const selector23 = createSelector(selector22, s => s) - const selector24 = createSelector(selector23, s => s) - const selector25 = createSelector(selector24, s => s) - const selector26 = createSelector(selector25, s => s) - const selector27 = createSelector(selector26, s => s) - const selector28 = createSelector(selector27, s => s) - const selector29 = createSelector(selector28, s => s) - const selector30 = createSelector(selector29, s => s) - selector30.dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].dependencies[0].dependencies[0] - .dependencies[0].dependencies[0].memoizedResultFunc.clearCache -} - -function deepNesting1() { - type State = { foo: string } - const readOne = (state: State) => state.foo - - const selector0 = createSelector(readOne, one => one) - const selector1 = createSelector([selector0], s => s) - const selector2 = createSelector([selector1], s => s) - const selector3 = createSelector([selector2], s => s) - const selector4 = createSelector([selector3], s => s) - const selector5 = createSelector([selector4], s => s) - const selector6 = createSelector([selector5], s => s) - const selector7 = createSelector([selector6], s => s) - const selector8 = createSelector([selector7], s => s) - const selector9 = createSelector([selector8], s => s) - const selector10 = createSelector([selector9], s => s) - const selector11 = createSelector([selector10], s => s) - const selector12 = createSelector([selector11], s => s) - const selector13 = createSelector([selector12], s => s) - const selector14 = createSelector([selector13], s => s) - const selector15 = createSelector([selector14], s => s) - const selector16 = createSelector([selector15], s => s) - const selector17 = createSelector([selector16], s => s) - const selector18 = createSelector([selector17], s => s) - const selector19 = createSelector([selector18], s => s) - const selector20 = createSelector([selector19], s => s) - const selector21 = createSelector([selector20], s => s) - const selector22 = createSelector([selector21], s => s) - const selector23 = createSelector([selector22], s => s) - const selector24 = createSelector([selector23], s => s) - const selector25 = createSelector([selector24], s => s) - const selector26 = createSelector([selector25], s => s) - const selector27 = createSelector([selector26], s => s) - const selector28 = createSelector([selector27], s => s) - const selector29 = createSelector([selector28], s => s) - const selector30 = createSelector([selector29], s => s) -} - -function deepNesting2() { - type State = { foo: string } - const readOne = (state: State) => state.foo - - const selector0 = createSelector(readOne, one => one) - const selector1 = createSelector(selector0, s => s, { - memoize: lruMemoize - }) - const selector2 = createSelector(selector1, s => s, { - memoize: lruMemoize - }) - const selector3 = createSelector(selector2, s => s, { - memoize: lruMemoize - }) - const selector4 = createSelector(selector3, s => s, { - memoize: lruMemoize - }) - const selector5 = createSelector(selector4, s => s, { - memoize: lruMemoize - }) - const selector6 = createSelector(selector5, s => s, { - memoize: lruMemoize - }) - const selector7 = createSelector(selector6, s => s, { - memoize: lruMemoize - }) - const selector8 = createSelector(selector7, s => s, { - memoize: lruMemoize - }) - const selector9 = createSelector(selector8, s => s, { - memoize: lruMemoize - }) - const selector10 = createSelector(selector9, s => s, { - memoize: lruMemoize - }) - const selector11 = createSelector(selector10, s => s, { - memoize: lruMemoize - }) - const selector12 = createSelector(selector11, s => s, { - memoize: lruMemoize - }) - const selector13 = createSelector(selector12, s => s, { - memoize: lruMemoize - }) - const selector14 = createSelector(selector13, s => s, { - memoize: lruMemoize - }) - const selector15 = createSelector(selector14, s => s, { - memoize: lruMemoize - }) - const selector16 = createSelector(selector15, s => s, { - memoize: lruMemoize - }) - const selector17 = createSelector(selector16, s => s, { - memoize: lruMemoize - }) - const selector18 = createSelector(selector17, s => s, { - memoize: lruMemoize - }) - const selector19 = createSelector(selector18, s => s, { - memoize: lruMemoize - }) - const selector20 = createSelector(selector19, s => s, { - memoize: lruMemoize - }) - const selector21 = createSelector(selector20, s => s, { - memoize: lruMemoize - }) - const selector22 = createSelector(selector21, s => s, { - memoize: lruMemoize - }) - const selector23 = createSelector(selector22, s => s, { - memoize: lruMemoize - }) - const selector24 = createSelector(selector23, s => s, { - memoize: lruMemoize - }) - const selector25 = createSelector(selector24, s => s, { - memoize: lruMemoize - }) - const selector26 = createSelector(selector25, s => s, { - memoize: lruMemoize - }) - const selector27 = createSelector(selector26, s => s, { - memoize: lruMemoize - }) - const selector28 = createSelector(selector27, s => s, { - memoize: lruMemoize - }) - const selector29 = createSelector(selector28, s => s, { - memoize: lruMemoize - }) -} - -function parameterLimit() { - const selector = createSelector( - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testBoolean: boolean }) => state.testBoolean, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testBoolean: boolean }) => state.testBoolean, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testBoolean: boolean }) => state.testBoolean, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testBoolean: boolean }) => state.testBoolean, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testString: string }) => state.testString, - (state: { testNumber: number }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray, - ( - foo1: string, - foo2: number, - foo3: boolean, - foo4: string, - foo5: string, - foo6: string, - foo7: string, - foo8: number, - foo9: string[], - foo10: string, - foo11: number, - foo12: boolean, - foo13: string, - foo14: string, - foo15: string, - foo16: string, - foo17: number, - foo18: string[], - foo19: string, - foo20: number, - foo21: boolean, - foo22: string, - foo23: string, - foo24: string, - foo25: string, - foo26: number, - foo27: string[], - foo28: string, - foo29: number, - foo30: boolean, - foo31: string, - foo32: string, - foo33: string, - foo34: string, - foo35: number, - foo36: string[] - ) => { - return { - foo1, - foo2, - foo3, - foo4, - foo5, - foo6, - foo7, - foo8, - foo9, - foo10, - foo11, - foo12, - foo13, - foo14, - foo15, - foo16, - foo17, - foo18, - foo19, - foo20, - foo21, - foo22, - foo23, - foo24, - foo25, - foo26, - foo27, - foo28, - foo29, - foo30, - foo31, - foo32, - foo33, - foo34, - foo35, - foo36 - } - } - ) -} +import memoizeOne from 'memoize-one' +import microMemoize from 'micro-memoize' +import { + unstable_autotrackMemoize as autotrackMemoize, + createSelector, + createSelectorCreator, + lruMemoize, + weakMapMemoize, +} from 'reselect' +import { expectExactType } from './typesTestUtils' + +interface RootState { + todos: { + id: number + completed: boolean + }[] +} +const state: RootState = { + todos: [ + { id: 0, completed: false }, + { id: 1, completed: false }, + ], +} + +function overrideOnlyMemoizeInCreateSelector() { + const selectorDefaultSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize }, + ) + const selectorDefaultArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: lruMemoize }, + ) + const selectorDefaultArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const selectorDefaultSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const selectorAutotrackSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: autotrackMemoize }, + ) + const selectorAutotrackArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: autotrackMemoize }, + ) + // @ts-expect-error When memoize is autotrackMemoize, type of memoizeOptions needs to be the same as options args in autotrackMemoize. + const selectorAutotrackArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: autotrackMemoize, memoizeOptions: { maxSize: 2 } }, + ) + // @ts-expect-error When memoize is autotrackMemoize, type of memoizeOptions needs to be the same as options args in autotrackMemoize. + const selectorAutotrackSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: autotrackMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const selectorWeakMapSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: weakMapMemoize }, + ) + const selectorWeakMapArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { memoize: weakMapMemoize }, + ) + // @ts-expect-error When memoize is weakMapMemoize, type of memoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } }, + ) + // @ts-expect-error When memoize is weakMapMemoize, type of memoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const createSelectorDefault = createSelectorCreator(lruMemoize) + const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) + const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) + const changeMemoizeMethodSelectorDefault = createSelectorDefault( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: weakMapMemoize }, + ) + const changeMemoizeMethodSelectorWeakMap = createSelectorWeakMap( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize }, + ) + const changeMemoizeMethodSelectorAutotrack = createSelectorAutotrack( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize }, + ) + const changeMemoizeMethodSelectorDefaultWithMemoizeOptions = + // @ts-expect-error When memoize is changed to weakMapMemoize or autotrackMemoize, memoizeOptions cannot be the same type as options args in lruMemoize. + createSelectorDefault( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { memoize: weakMapMemoize, memoizeOptions: { maxSize: 2 } }, + ) + const changeMemoizeMethodSelectorWeakMapWithMemoizeOptions = + createSelectorWeakMap( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } }, // When memoize is changed to lruMemoize, memoizeOptions can now be the same type as options args in lruMemoize. + ) + const changeMemoizeMethodSelectorAutotrackWithMemoizeOptions = + createSelectorAutotrack( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { memoize: lruMemoize, memoizeOptions: { maxSize: 2 } }, // When memoize is changed to lruMemoize, memoizeOptions can now be the same type as options args in lruMemoize. + ) +} + +function overrideOnlyArgsMemoizeInCreateSelector() { + const selectorDefaultSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize }, + ) + const selectorDefaultArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize }, + ) + const selectorDefaultArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + const selectorDefaultSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + const selectorAutotrackSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: autotrackMemoize }, + ) + const selectorAutotrackArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { argsMemoize: autotrackMemoize }, + ) + // @ts-expect-error When argsMemoize is autotrackMemoize, type of argsMemoizeOptions needs to be the same as options args in autotrackMemoize. + const selectorAutotrackArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { + argsMemoize: autotrackMemoize, + argsMemoizeOptions: { maxSize: 2 }, + }, + ) + // @ts-expect-error When argsMemoize is autotrackMemoize, type of argsMemoizeOptions needs to be the same as options args in autotrackMemoize. + const selectorAutotrackSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + argsMemoize: autotrackMemoize, + argsMemoizeOptions: { maxSize: 2 }, + }, + ) + const selectorWeakMapSeparateInlineArgs = createSelector( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize }, + ) + const selectorWeakMapArgsAsArray = createSelector( + [(state: RootState) => state.todos], + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize }, + ) + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapArgsAsArrayWithMemoizeOptions = createSelector( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions = createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions1 = createSelector( + [ + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + ], + { + argsMemoize: weakMapMemoize, + argsMemoizeOptions: { maxSize: 2 }, + }, + ) + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions2 = createSelector( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: weakMapMemoize, + memoizeOptions: { + equalityCheck: + // @ts-expect-error + (a, b) => a === b, + maxSize: 2, + }, + argsMemoizeOptions: { maxSize: 2 }, + }, + ) + + const createSelectorLruMemoize = createSelectorCreator({ + memoize: lruMemoize, + }) + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions3 = + // @ts-expect-error When argsMemoize is weakMapMemoize, type of argsMemoizeOptions needs to be the same as options args in weakMapMemoize. + createSelectorLruMemoize( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: weakMapMemoize, + // memoizeOptions: [], + memoizeOptions: [ + { + equalityCheck: + // @ts-expect-error + (a, b) => a === b, + maxSize: 2, + }, + ], + argsMemoizeOptions: [{ maxSize: 2 }], + }, + ) + + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions5 = + // @ts-expect-error + createSelectorLruMemoize( + [(state: RootState) => state.todos], + // @ts-expect-error + todos => todos.map(t => t.id), + { + argsMemoize: weakMapMemoize, + memoizeOptions: [{ isPromise: false }], + argsMemoizeOptions: [], + }, + ) + const selectorWeakMapSeparateInlineArgsWithMemoizeOptions6 = + createSelectorLruMemoize( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { + argsMemoize: weakMapMemoize, + memoize: weakMapMemoize, + memoizeOptions: [], + argsMemoizeOptions: [], + // argsMemoizeOptions: (a, b) => a === b + }, + ) + const createSelectorDefault = createSelectorCreator(lruMemoize) + const createSelectorWeakMap = createSelectorCreator(weakMapMemoize) + const createSelectorAutotrack = createSelectorCreator(autotrackMemoize) + const changeMemoizeMethodSelectorDefault = createSelectorDefault( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize }, + ) + const changeMemoizeMethodSelectorWeakMap = createSelectorWeakMap( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize }, + ) + const changeMemoizeMethodSelectorAutotrack = createSelectorAutotrack( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize }, + ) + const changeMemoizeMethodSelectorDefaultWithMemoizeOptions = + // @ts-expect-error When argsMemoize is changed to weakMapMemoize or autotrackMemoize, argsMemoizeOptions cannot be the same type as options args in lruMemoize. + createSelectorDefault( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { argsMemoize: weakMapMemoize, argsMemoizeOptions: { maxSize: 2 } }, + ) + const changeMemoizeMethodSelectorWeakMapWithMemoizeOptions = + createSelectorWeakMap( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } }, // When argsMemoize is changed to lruMemoize, argsMemoizeOptions can now be the same type as options args in lruMemoize. + ) + const changeMemoizeMethodSelectorAutotrackWithMemoizeOptions = + createSelectorAutotrack( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { argsMemoize: lruMemoize, argsMemoizeOptions: { maxSize: 2 } }, // When argsMemoize is changed to lruMemoize, argsMemoizeOptions can now be the same type as options args in lruMemoize. + ) +} + +function overrideMemoizeAndArgsMemoizeInCreateSelector() { + const createSelectorMicroMemoize = createSelectorCreator({ + memoize: microMemoize, + memoizeOptions: [{ isEqual: (a, b) => a === b }], + // memoizeOptions: { isEqual: (a, b) => a === b }, + argsMemoize: microMemoize, + argsMemoizeOptions: { isEqual: (a, b) => a === b }, + }) + const selectorMicroMemoize = createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(({ id }) => id), + ) + expectExactType(selectorMicroMemoize(state)) + // @ts-expect-error + selectorMicroMemoize() + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoize.cache + selectorMicroMemoize.fn() + selectorMicroMemoize.isMemoized + selectorMicroMemoize.options + // @ts-expect-error + selectorMicroMemoize.clearCache() + // Checking existence of fields related to `memoize` + selectorMicroMemoize.memoizedResultFunc.cache + selectorMicroMemoize.memoizedResultFunc.fn() + selectorMicroMemoize.memoizedResultFunc.isMemoized + selectorMicroMemoize.memoizedResultFunc.options + // @ts-expect-error + selectorMicroMemoize.memoizedResultFunc.clearCache() + // Checking existence of fields related to the actual memoized selector + selectorMicroMemoize.dependencies + expectExactType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoize.dependencies) + expectExactType(selectorMicroMemoize.lastResult()) + // @ts-expect-error + selectorMicroMemoize.memoizedResultFunc() + expectExactType( + selectorMicroMemoize.memoizedResultFunc([{ id: 0, completed: true }]), + ) + selectorMicroMemoize.recomputations() + selectorMicroMemoize.resetRecomputations() + // @ts-expect-error + selectorMicroMemoize.resultFunc() + expectExactType( + selectorMicroMemoize.resultFunc([{ id: 0, completed: true }]), + ) + + // Checking to see if types dynamically change if memoize or argsMemoize are overridden inside `createSelector`. + // `microMemoize` was initially passed into `createSelectorCreator` + // as `memoize` and `argsMemoize`, After overriding them both to `lruMemoize`, + // not only does the type for `memoizeOptions` and `argsMemoizeOptions` change to + // the options parameter of `lruMemoize`, the output selector fields + // also change their type to the return type of `lruMemoize`. + const selectorMicroMemoizeOverridden = createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 2 }, + argsMemoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 3 }, + }, + ) + expectExactType(selectorMicroMemoizeOverridden(state)) + // @ts-expect-error + selectorMicroMemoizeOverridden() + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoizeOverridden.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.options + // Checking existence of fields related to `memoize` + selectorMicroMemoizeOverridden.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.memoizedResultFunc.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.memoizedResultFunc.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.memoizedResultFunc.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverridden.memoizedResultFunc.options + // Checking existence of fields related to the actual memoized selector + expectExactType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeOverridden.dependencies) + expectExactType( + selectorMicroMemoizeOverridden.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeOverridden.memoizedResultFunc() + selectorMicroMemoizeOverridden.recomputations() + selectorMicroMemoizeOverridden.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeOverridden.resultFunc() + expectExactType( + selectorMicroMemoizeOverridden.resultFunc([{ id: 0, completed: true }]), + ) + // Making sure the type behavior is consistent when args are passed in as an array. + const selectorMicroMemoizeOverriddenArray = createSelectorMicroMemoize( + [(state: RootState) => state.todos], + todos => todos.map(({ id }) => id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 2 }, + argsMemoizeOptions: { equalityCheck: (a, b) => a === b, maxSize: 3 }, + }, + ) + expectExactType(selectorMicroMemoizeOverriddenArray(state)) + // @ts-expect-error + selectorMicroMemoizeOverriddenArray() + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoizeOverriddenArray.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.options + // Checking existence of fields related to `memoize` + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverriddenArray.memoizedResultFunc.options + // Checking existence of fields related to the actual memoized selector + expectExactType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeOverriddenArray.dependencies) + expectExactType( + selectorMicroMemoizeOverriddenArray.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeOverriddenArray.memoizedResultFunc() + selectorMicroMemoizeOverriddenArray.recomputations() + selectorMicroMemoizeOverriddenArray.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeOverriddenArray.resultFunc() + expectExactType( + selectorMicroMemoizeOverriddenArray.resultFunc([ + { id: 0, completed: true }, + ]), + ) + const selectorMicroMemoizeOverrideArgsMemoizeOnlyWrong = + // @ts-expect-error Because `memoizeOptions` should not contain `resultEqualityCheck`. + createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(({ id }) => id), + { + argsMemoize: lruMemoize, + memoizeOptions: { + isPromise: false, + resultEqualityCheck: + // @ts-expect-error + (a, b) => a === b, + }, + argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }, + ) + const selectorMicroMemoizeOverrideArgsMemoizeOnly = + createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(({ id }) => id), + { + argsMemoize: lruMemoize, + memoizeOptions: { isPromise: false }, + argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }, + ) + expectExactType(selectorMicroMemoizeOverrideArgsMemoizeOnly(state)) + // @ts-expect-error + selectorMicroMemoizeOverrideArgsMemoizeOnly() + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoizeOverrideArgsMemoizeOnly.clearCache() // Prior to override, this field did NOT exist. + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideArgsMemoizeOnly.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideArgsMemoizeOnly.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideArgsMemoizeOnly.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideArgsMemoizeOnly.options + + // Checking existence of fields related to `memoize`, these should still be the same. + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.cache + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.fn() + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.isMemoized + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.options + // @ts-expect-error Note that since we did not override `memoize` in the options object, + // `memoizedResultFunc.clearCache` is still an invalid field access. + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc.clearCache() + // Checking existence of fields related to the actual memoized selector + expectExactType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeOverrideArgsMemoizeOnly.dependencies) + expectExactType( + selectorMicroMemoizeOverrideArgsMemoizeOnly.lastResult(), + ) + expectExactType( + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeOverrideArgsMemoizeOnly.memoizedResultFunc() + selectorMicroMemoizeOverrideArgsMemoizeOnly.recomputations() + selectorMicroMemoizeOverrideArgsMemoizeOnly.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc() + expectExactType( + selectorMicroMemoizeOverrideArgsMemoizeOnly.resultFunc([ + { id: 0, completed: true }, + ]), + ) + + const selectorMicroMemoizeOverrideMemoizeOnly = createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + memoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }, + ) + expectExactType(selectorMicroMemoizeOverrideMemoizeOnly(state)) + // @ts-expect-error + selectorMicroMemoizeOverrideMemoizeOnly() + + // Checking existence of fields related to `argsMemoize` + selectorMicroMemoizeOverrideMemoizeOnly.cache + selectorMicroMemoizeOverrideMemoizeOnly.fn + selectorMicroMemoizeOverrideMemoizeOnly.isMemoized + selectorMicroMemoizeOverrideMemoizeOnly.options + // @ts-expect-error Note that since we did not override `argsMemoize` in the options object, + // `selector.clearCache` is still an invalid field access. + selectorMicroMemoizeOverrideMemoizeOnly.clearCache() + + // Checking existence of fields related to `memoize` + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.cache + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.fn() + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.isMemoized + // @ts-expect-error Prior to override, this field DID exist. + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.options + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc.clearCache() // Prior to override, this field did NOT exist. + + // Checking existence of fields related to the actual memoized selector + expectExactType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeOverrideMemoizeOnly.dependencies) + expectExactType( + selectorMicroMemoizeOverrideMemoizeOnly.lastResult(), + ) + expectExactType( + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeOverrideMemoizeOnly.memoizedResultFunc() + selectorMicroMemoizeOverrideMemoizeOnly.recomputations() + selectorMicroMemoizeOverrideMemoizeOnly.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeOverrideMemoizeOnly.resultFunc() + expectExactType( + selectorMicroMemoizeOverrideMemoizeOnly.resultFunc([ + { id: 0, completed: true }, + ]), + ) + + const selectorMicroMemoizePartiallyOverridden = + // @ts-expect-error Since `argsMemoize` is set to `lruMemoize`, `argsMemoizeOptions` must match the options object parameter of `lruMemoize` + createSelectorMicroMemoize( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: { + equalityCheck: + // @ts-expect-error + (a, b) => a === b, + maxSize: 2, + }, + argsMemoizeOptions: { isPromise: false }, // This field causes a type error since it does not match the options param of `lruMemoize`. + }, + ) + const selectorMicroMemoizePartiallyOverridden1 = + // @ts-expect-error Since `argsMemoize` is set to `lruMemoize`, `argsMemoizeOptions` must match the options object parameter of `lruMemoize` + createSelectorMicroMemoize( + (state: RootState) => state.todos, + // @ts-expect-error + todos => todos.map(t => t.id), + { + memoize: lruMemoize, + argsMemoize: lruMemoize, + memoizeOptions: [ + { + equalityCheck: + // @ts-expect-error + (a, b) => a === b, + maxSize: 2, + }, + ], + argsMemoizeOptions: [{ isPromise: false }], // This field causes a type error since it does not match the options param of `lruMemoize`. + }, + ) + const selectorMicroMemoizePartiallyOverridden2 = createSelectorMicroMemoize( + (state: RootState) => state.todos, + todos => todos.map(t => t.id), + { + argsMemoizeOptions: [{ isPromise: false }], + }, + ) + + const selectorDefaultParametric = createSelector( + (state: RootState, id: number) => id, + (state: RootState) => state.todos, + (id, todos) => todos.filter(todo => todo.id === id), + { + argsMemoize: microMemoize, + devModeChecks: { inputStabilityCheck: 'never' }, + memoize: memoizeOne, + argsMemoizeOptions: [], + memoizeOptions: [(a, b) => a === b], + }, + ) + expectExactType< + { + id: number + completed: boolean + }[] + >(selectorDefaultParametric(state, 0)) + expectExactType< + { + id: number + completed: boolean + }[] + >(selectorDefaultParametric(state, 1)) + // @ts-expect-error + selectorDefaultParametric(state) + // @ts-expect-error + selectorDefaultParametric(1) + // @ts-expect-error + selectorDefaultParametric(state, '') + // @ts-expect-error + selectorDefaultParametric(state, 1, 1) + // Checking existence of fields related to `argsMemoize` + // Prior to override, this field did NOT exist. + selectorDefaultParametric.cache + // Prior to override, this field did NOT exist. + selectorDefaultParametric.fn + // Prior to override, this field did NOT exist. + selectorDefaultParametric.isMemoized + // Prior to override, this field did NOT exist. + selectorDefaultParametric.options + // @ts-expect-error Prior to override, this field DID exist. + selectorDefaultParametric.clearCache() + + // Checking existence of fields related to `memoize` + // @ts-expect-error Prior to override, this field DID exist. + selectorDefaultParametric.memoizedResultFunc.clearCache() + // Prior to override, this field did NOT exist. + selectorDefaultParametric.memoizedResultFunc.clear() + + // Checking existence of fields related to the actual memoized selector + expectExactType< + [ + (state: RootState, id: number) => number, + (state: RootState) => { id: number; completed: boolean }[], + ] + >(selectorDefaultParametric.dependencies) + expectExactType<{ id: number; completed: boolean }[]>( + selectorDefaultParametric.lastResult(), + ) + expectExactType<{ id: number; completed: boolean }[]>( + selectorDefaultParametric.memoizedResultFunc(0, [ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorDefaultParametric.memoizedResultFunc() + selectorDefaultParametric.recomputations() + selectorDefaultParametric.resetRecomputations() + // @ts-expect-error + selectorDefaultParametric.resultFunc() + expectExactType<{ id: number; completed: boolean }[]>( + selectorDefaultParametric.resultFunc(0, [{ id: 0, completed: true }]), + ) +} + +function memoizeAndArgsMemoizeInCreateSelectorCreator() { + // If we don't pass in `argsMemoize`, the type for `argsMemoizeOptions` + // falls back to the options parameter of `lruMemoize`. + const createSelectorArgsMemoizeOptionsFallbackToDefault = + createSelectorCreator({ + memoize: microMemoize, + memoizeOptions: [{ isPromise: false }], + argsMemoizeOptions: { resultEqualityCheck: (a, b) => a === b }, + }) + const selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault = + createSelectorArgsMemoizeOptionsFallbackToDefault( + (state: RootState) => state.todos, + todos => todos.map(({ id }) => id), + ) + expectExactType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault(state), + ) + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault() + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.clearCache() + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.cache + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.fn + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.isMemoized + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.options + // Checking existence of fields related to `memoize` + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc + .cache + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc.fn() + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc + .isMemoized + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc + .options + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc.clearCache() + // Checking existence of fields related to the actual memoized selector + expectExactType< + [ + (state: RootState) => { + id: number + completed: boolean + }[], + ] + >(selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.dependencies) + expectExactType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.lastResult(), + ) + expectExactType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc([ + { id: 0, completed: true }, + ]), + ) + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoizedResultFunc() + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.recomputations() + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resetRecomputations() + // @ts-expect-error + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc() + expectExactType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.resultFunc([ + { id: 0, completed: true }, + ]), + ) + expectExactType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.memoize, + ) + expectExactType( + selectorMicroMemoizeArgsMemoizeOptionsFallbackToDefault.argsMemoize, + ) + + const createSelectorWithWrongArgsMemoizeOptions = + // @ts-expect-error If we don't pass in `argsMemoize`, the type for `argsMemoizeOptions` falls back to the options parameter of `weakMapMemoize`. + createSelectorCreator({ + memoize: microMemoize, + memoizeOptions: { isEqual: (a, b) => a === b }, + argsMemoizeOptions: { + isEqual: + // @ts-expect-error implicit any + (a, b) => a === b, + }, + }) + + // When passing in an options object as the first argument, there should be no other arguments. + const createSelectorWrong = createSelectorCreator( + { + // @ts-expect-error + memoize: microMemoize, + // @ts-expect-error + memoizeOptions: { isEqual: (a, b) => a === b }, + // @ts-expect-error + argsMemoizeOptions: { equalityCheck: (a, b) => a === b }, + }, + [], // This causes the error. + ) +} + +function deepNesting() { + type State = { foo: string } + const readOne = (state: State) => state.foo + + const selector0 = createSelector(readOne, one => one) + const selector1 = createSelector(selector0, s => s) + const selector2 = createSelector(selector1, s => s) + const selector3 = createSelector(selector2, s => s) + const selector4 = createSelector(selector3, s => s) + const selector5 = createSelector(selector4, s => s) + const selector6 = createSelector(selector5, s => s) + const selector7 = createSelector(selector6, s => s) + const selector8 = createSelector(selector7, s => s) + const selector9 = createSelector(selector8, s => s) + const selector10 = createSelector(selector9, s => s, { + memoize: microMemoize, + }) + selector10.dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].memoizedResultFunc.clearCache + const selector11 = createSelector(selector10, s => s) + const selector12 = createSelector(selector11, s => s) + const selector13 = createSelector(selector12, s => s) + const selector14 = createSelector(selector13, s => s) + const selector15 = createSelector(selector14, s => s) + const selector16 = createSelector(selector15, s => s) + const selector17 = createSelector(selector16, s => s) + const selector18 = createSelector(selector17, s => s) + const selector19 = createSelector(selector18, s => s) + const selector20 = createSelector(selector19, s => s) + selector20.dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].memoizedResultFunc.cache + const selector21 = createSelector(selector20, s => s) + const selector22 = createSelector(selector21, s => s) + const selector23 = createSelector(selector22, s => s) + const selector24 = createSelector(selector23, s => s) + const selector25 = createSelector(selector24, s => s) + const selector26 = createSelector(selector25, s => s) + const selector27 = createSelector(selector26, s => s) + const selector28 = createSelector(selector27, s => s) + const selector29 = createSelector(selector28, s => s) + const selector30 = createSelector(selector29, s => s) + selector30.dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].dependencies[0].dependencies[0] + .dependencies[0].dependencies[0].memoizedResultFunc.clearCache +} + +function deepNesting1() { + type State = { foo: string } + const readOne = (state: State) => state.foo + + const selector0 = createSelector(readOne, one => one) + const selector1 = createSelector([selector0], s => s) + const selector2 = createSelector([selector1], s => s) + const selector3 = createSelector([selector2], s => s) + const selector4 = createSelector([selector3], s => s) + const selector5 = createSelector([selector4], s => s) + const selector6 = createSelector([selector5], s => s) + const selector7 = createSelector([selector6], s => s) + const selector8 = createSelector([selector7], s => s) + const selector9 = createSelector([selector8], s => s) + const selector10 = createSelector([selector9], s => s) + const selector11 = createSelector([selector10], s => s) + const selector12 = createSelector([selector11], s => s) + const selector13 = createSelector([selector12], s => s) + const selector14 = createSelector([selector13], s => s) + const selector15 = createSelector([selector14], s => s) + const selector16 = createSelector([selector15], s => s) + const selector17 = createSelector([selector16], s => s) + const selector18 = createSelector([selector17], s => s) + const selector19 = createSelector([selector18], s => s) + const selector20 = createSelector([selector19], s => s) + const selector21 = createSelector([selector20], s => s) + const selector22 = createSelector([selector21], s => s) + const selector23 = createSelector([selector22], s => s) + const selector24 = createSelector([selector23], s => s) + const selector25 = createSelector([selector24], s => s) + const selector26 = createSelector([selector25], s => s) + const selector27 = createSelector([selector26], s => s) + const selector28 = createSelector([selector27], s => s) + const selector29 = createSelector([selector28], s => s) + const selector30 = createSelector([selector29], s => s) +} + +function deepNesting2() { + type State = { foo: string } + const readOne = (state: State) => state.foo + + const selector0 = createSelector(readOne, one => one) + const selector1 = createSelector(selector0, s => s, { + memoize: lruMemoize, + }) + const selector2 = createSelector(selector1, s => s, { + memoize: lruMemoize, + }) + const selector3 = createSelector(selector2, s => s, { + memoize: lruMemoize, + }) + const selector4 = createSelector(selector3, s => s, { + memoize: lruMemoize, + }) + const selector5 = createSelector(selector4, s => s, { + memoize: lruMemoize, + }) + const selector6 = createSelector(selector5, s => s, { + memoize: lruMemoize, + }) + const selector7 = createSelector(selector6, s => s, { + memoize: lruMemoize, + }) + const selector8 = createSelector(selector7, s => s, { + memoize: lruMemoize, + }) + const selector9 = createSelector(selector8, s => s, { + memoize: lruMemoize, + }) + const selector10 = createSelector(selector9, s => s, { + memoize: lruMemoize, + }) + const selector11 = createSelector(selector10, s => s, { + memoize: lruMemoize, + }) + const selector12 = createSelector(selector11, s => s, { + memoize: lruMemoize, + }) + const selector13 = createSelector(selector12, s => s, { + memoize: lruMemoize, + }) + const selector14 = createSelector(selector13, s => s, { + memoize: lruMemoize, + }) + const selector15 = createSelector(selector14, s => s, { + memoize: lruMemoize, + }) + const selector16 = createSelector(selector15, s => s, { + memoize: lruMemoize, + }) + const selector17 = createSelector(selector16, s => s, { + memoize: lruMemoize, + }) + const selector18 = createSelector(selector17, s => s, { + memoize: lruMemoize, + }) + const selector19 = createSelector(selector18, s => s, { + memoize: lruMemoize, + }) + const selector20 = createSelector(selector19, s => s, { + memoize: lruMemoize, + }) + const selector21 = createSelector(selector20, s => s, { + memoize: lruMemoize, + }) + const selector22 = createSelector(selector21, s => s, { + memoize: lruMemoize, + }) + const selector23 = createSelector(selector22, s => s, { + memoize: lruMemoize, + }) + const selector24 = createSelector(selector23, s => s, { + memoize: lruMemoize, + }) + const selector25 = createSelector(selector24, s => s, { + memoize: lruMemoize, + }) + const selector26 = createSelector(selector25, s => s, { + memoize: lruMemoize, + }) + const selector27 = createSelector(selector26, s => s, { + memoize: lruMemoize, + }) + const selector28 = createSelector(selector27, s => s, { + memoize: lruMemoize, + }) + const selector29 = createSelector(selector28, s => s, { + memoize: lruMemoize, + }) +} + +function parameterLimit() { + const selector = createSelector( + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testBoolean: boolean }) => state.testBoolean, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testStringArray: string[] }) => state.testStringArray, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testBoolean: boolean }) => state.testBoolean, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testStringArray: string[] }) => state.testStringArray, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testBoolean: boolean }) => state.testBoolean, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testStringArray: string[] }) => state.testStringArray, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testBoolean: boolean }) => state.testBoolean, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testString: string }) => state.testString, + (state: { testNumber: number }) => state.testNumber, + (state: { testStringArray: string[] }) => state.testStringArray, + ( + foo1: string, + foo2: number, + foo3: boolean, + foo4: string, + foo5: string, + foo6: string, + foo7: string, + foo8: number, + foo9: string[], + foo10: string, + foo11: number, + foo12: boolean, + foo13: string, + foo14: string, + foo15: string, + foo16: string, + foo17: number, + foo18: string[], + foo19: string, + foo20: number, + foo21: boolean, + foo22: string, + foo23: string, + foo24: string, + foo25: string, + foo26: number, + foo27: string[], + foo28: string, + foo29: number, + foo30: boolean, + foo31: string, + foo32: string, + foo33: string, + foo34: string, + foo35: number, + foo36: string[], + ) => { + return { + foo1, + foo2, + foo3, + foo4, + foo5, + foo6, + foo7, + foo8, + foo9, + foo10, + foo11, + foo12, + foo13, + foo14, + foo15, + foo16, + foo17, + foo18, + foo19, + foo20, + foo21, + foo22, + foo23, + foo24, + foo25, + foo26, + foo27, + foo28, + foo29, + foo30, + foo31, + foo32, + foo33, + foo34, + foo35, + foo36, + } + }, + ) +} diff --git a/typescript_test/test.ts b/typescript_test/test.ts index 0795f2e70..e400c9f45 100644 --- a/typescript_test/test.ts +++ b/typescript_test/test.ts @@ -11,26 +11,25 @@ import type { GetStateFromSelectors, Selector, SelectorResultArray, - TypedStructuredSelectorCreator + TypedStructuredSelectorCreator, } from 'reselect' import { createSelector, createSelectorCreator, createStructuredSelector, lruMemoize, - referenceEqualityCheck + referenceEqualityCheck, } from 'reselect' import { expectExactType } from './typesTestUtils' -type Exact = (() => T extends A ? 1 : 0) extends () => T extends B - ? 1 - : 0 - ? A extends B - ? B extends A - ? unknown +type Exact = + (() => T extends A ? 1 : 0) extends () => T extends B ? 1 : 0 + ? A extends B + ? B extends A + ? unknown + : never : never : never - : never interface StateA { a: number @@ -50,13 +49,13 @@ interface StateSub { // Test exporting export const testExportBasic = createSelector( (state: StateA) => state.a, - a => a + a => a, ) // Test for exporting declaration of created selector creator export const testExportStructured = createSelectorCreator( lruMemoize, - (a, b) => typeof a === typeof b + (a, b) => typeof a === typeof b, ) function testSelector() { @@ -64,7 +63,7 @@ function testSelector() { const selector = createSelector( (state: State) => state.foo, - foo => foo + foo => foo, ) const res = selector.resultFunc('test') @@ -83,13 +82,13 @@ function testSelector() { createSelector( (state: { foo: string }) => state.foo, (state: { bar: number }) => state.bar, - (foo, bar) => 1 + (foo, bar) => 1, ) const selectorWithUnions = createSelector( (state: State, val: string | number) => state.foo, (state: State, val: string | number) => val, - (foo, val) => val + (foo, val) => val, ) } @@ -100,7 +99,7 @@ function testNestedSelector() { createSelector( (state: State) => state.foo, (state: State) => state.bar, - (foo, bar) => ({ foo, bar }) + (foo, bar) => ({ foo, bar }), ), (state: State) => state.baz, ({ foo, bar }, baz) => { @@ -115,7 +114,7 @@ function testNestedSelector() { const baz1: boolean = baz // @ts-expect-error const baz2: string = baz - } + }, ) } @@ -125,7 +124,7 @@ function testSelectorAsCombiner() { const subSelector = createSelector( (state: SubState) => state.foo, - foo => foo + foo => foo, ) const selector = createSelector((state: State) => state.bar, subSelector) @@ -142,15 +141,15 @@ function testSelectorAsCombiner() { type Component

= (props: P) => any declare function connect( - selector: Selector + selector: Selector, ): (component: Component

) => Component

function testConnect() { connect( createSelector( (state: { foo: string }) => state.foo, - foo => ({ foo }) - ) + foo => ({ foo }), + ), )(props => { // @ts-expect-error props.bar @@ -161,7 +160,7 @@ function testConnect() { const selector2 = createSelector( (state: { foo: string }) => state.foo, (state: { baz: number }, props: { bar: number }) => props.bar, - (foo, bar) => ({ foo, baz: bar }) + (foo, bar) => ({ foo, baz: bar }), ) const connected = connect(selector2)(props => { @@ -182,7 +181,7 @@ function testInvalidTypeInCombinator() { // @ts-expect-error createSelector( (state: { foo: string }) => state.foo, - (foo: number) => foo + (foo: number) => foo, ) createSelector( @@ -190,7 +189,7 @@ function testInvalidTypeInCombinator() { (state: any) => state.bar, (state: any) => state.baz, // @ts-expect-error - (foo: string, bar: number, baz: boolean, fizz: string) => {} + (foo: string, bar: number, baz: boolean, fizz: string) => {}, ) // does not allow heterogeneous parameter type @@ -215,10 +214,10 @@ function testInvalidTypeInCombinator() { foo6: string, foo7: string, foo8: number, - foo9: string[] + foo9: string[], ) => { return { foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9 } - } + }, ) // does not allow a large array of heterogeneous parameter type @@ -234,7 +233,7 @@ function testInvalidTypeInCombinator() { (state: { testString: string }) => state.testString, (state: { testString: string }) => state.testString, (state: { testNumber: string }) => state.testNumber, - (state: { testStringArray: string[] }) => state.testStringArray + (state: { testStringArray: string[] }) => state.testStringArray, ], ( foo1: string, @@ -245,10 +244,10 @@ function testInvalidTypeInCombinator() { foo6: string, foo7: string, foo8: number, - foo9: string[] + foo9: string[], ) => { return { foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9 } - } + }, ) } @@ -276,23 +275,23 @@ function testParametricSelector() { foo6: string, foo7: string, foo8: string, - foo9: string[] + foo9: string[], ) => { return { foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9 } - } + }, ) const res1 = selector1({ testString: 'a', testNumber: 42, testBoolean: true, - testStringArray: ['b', 'c'] + testStringArray: ['b', 'c'], }) const selector = createSelector( (state: State) => state.foo, (state: State, props: Props) => props.bar, - (foo, bar) => ({ foo, bar }) + (foo, bar) => ({ foo, bar }), ) // @ts-expect-error @@ -317,8 +316,8 @@ function testParametricSelector() { foo3, foo4, foo5, - bar - }) + bar, + }), ) selector2({ foo: 'fizz' }, { bar: 42 }) @@ -329,7 +328,7 @@ function testParametricSelector() { (s: State, y: number) => y, (v, x) => { return x + v - } + }, ) // @ts-expect-error @@ -340,7 +339,7 @@ function testParametricSelector() { (s: State, val: string | number) => val, (foo, val) => { return val - } + }, ) selector4({ foo: 'fizz' }, 42) @@ -351,9 +350,9 @@ function testArrayArgument() { [ (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, - (state: { foo: string }, props: { bar: number }) => props.bar + (state: { foo: string }, props: { bar: number }) => props.bar, ], - (foo1, foo2, bar) => ({ foo1, foo2, bar }) + (foo1, foo2, bar) => ({ foo1, foo2, bar }), ) const ret = selector({ foo: 'fizz' }, { bar: 42 }) @@ -368,9 +367,9 @@ function testArrayArgument() { createSelector( [ (state: { foo: string }) => state.foo, - (state: { foo: string }) => state.foo + (state: { foo: string }) => state.foo, ], - (foo: string, bar: number) => {} + (foo: string, bar: number) => {}, ) createSelector( @@ -384,7 +383,7 @@ function testArrayArgument() { (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, - (state: { foo: string }) => state.foo + (state: { foo: string }) => state.foo, ], ( foo1: string, @@ -396,8 +395,8 @@ function testArrayArgument() { foo7: string, foo8: string, foo9: string, - foo10: string - ) => {} + foo10: string, + ) => {}, ) // @ts-expect-error @@ -412,9 +411,9 @@ function testArrayArgument() { (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, - (state: { foo: string }) => state.foo + (state: { foo: string }) => state.foo, ], - (foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8: number, foo9, foo10) => {} + (foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8: number, foo9, foo10) => {}, ) // @ts-expect-error @@ -435,11 +434,11 @@ function testArrayArgument() { state => state.foo, // @ts-expect-error state => state.foo, - 1 + 1, ], // We expect an error here, but the error differs between TS versions // @ts-ignore - (foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9) => {} + (foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9) => {}, ) const selector2 = createSelector( @@ -452,7 +451,7 @@ function testArrayArgument() { (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, - (state: { foo: string }) => state.foo + (state: { foo: string }) => state.foo, ], ( foo1: string, @@ -463,10 +462,10 @@ function testArrayArgument() { foo6: string, foo7: string, foo8: string, - foo9: string + foo9: string, ) => { return { foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9 } - } + }, ) { @@ -497,7 +496,7 @@ function testArrayArgument() { (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, (state: { foo: string }) => state.foo, - (state: { foo: string }) => state.foo + (state: { foo: string }) => state.foo, ], ( bar: number, @@ -508,10 +507,10 @@ function testArrayArgument() { foo5: string, foo6: string, foo7: string, - foo8: string + foo8: string, ) => { return { foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, bar } - } + }, ) // allows a large array of heterogeneous parameter type selectors @@ -525,7 +524,7 @@ function testArrayArgument() { (state: { testString: string }) => state.testString, (state: { testString: string }) => state.testString, (state: { testString: string }) => state.testString, - (state: { testStringArray: string[] }) => state.testStringArray + (state: { testStringArray: string[] }) => state.testStringArray, ], ( foo1: string, @@ -536,10 +535,10 @@ function testArrayArgument() { foo6: string, foo7: string, foo8: string, - foo9: string[] + foo9: string[], ) => { return { foo1, foo2, foo3, foo4, foo5, foo6, foo7, foo8, foo9 } - } + }, ) // @ts-expect-error @@ -572,7 +571,7 @@ function testOptionalArgumentsConflicting() { const baz1: boolean = baz // @ts-expect-error const baz2: number = baz - } + }, ) // @ts-expect-error the selector above has inconsistent conflicting arguments so usage should error @@ -584,7 +583,7 @@ function testOptionalArgumentsConflicting() { const selector2 = createSelector( (state: State, prefix: any) => prefix + state.foo, - str => str + str => str, ) // @ts-expect-error here we require one argument which can be anything so error if there are no arguments @@ -596,7 +595,7 @@ function testOptionalArgumentsConflicting() { // here the argument is optional so it should be possible to omit the argument or pass anything const selector3 = createSelector( (state: State, prefix?: any) => prefix + state.foo, - str => str + str => str, ) selector3({} as State) @@ -607,7 +606,7 @@ function testOptionalArgumentsConflicting() { const selector4 = createSelector( (state: State, prefix: string, suffix: any) => prefix + state.foo + String(suffix), - str => str + str => str, ) // @ts-expect-error @@ -620,7 +619,7 @@ function testOptionalArgumentsConflicting() { const selector5 = createSelector( (state: State, prefix: string, suffix: unknown) => prefix + state.foo + String(suffix), - str => str + str => str, ) // @ts-expect-error @@ -650,9 +649,9 @@ function testOptionalArgumentsConflicting() { ( state: State, // eslint-disable-next-line @typescript-eslint/no-inferrable-types - prefix: string = 'a' + prefix: string = 'a', ) => prefix + state.foo, - (str: string) => str + (str: string) => str, ) selector7({} as State) @@ -662,7 +661,7 @@ function testOptionalArgumentsConflicting() { const selector8 = createSelector( (state: State, prefix: unknown) => prefix, - str => str + str => str, ) // @ts-expect-error needs a argument @@ -684,11 +683,11 @@ function testLruMemoize() { const memoized2 = lruMemoize( (str: string, arr: string[]): { str: string; arr: string[] } => ({ str, - arr + arr, }), (a: T, b: T) => { return `${a}` === `${b}` - } + }, ) const ret2 = memoized2('', ['1', '2']) @@ -701,7 +700,7 @@ function testCreateSelectorCreator() { const selector = defaultCreateSelector( (state: { foo: string }) => state.foo, - foo => foo + foo => foo, ) const value: string = selector({ foo: 'fizz' }) @@ -714,7 +713,7 @@ function testCreateSelectorCreator() { const parametric = defaultCreateSelector( (state: { foo: string }) => state.foo, (state: { foo: string }, props: { bar: number }) => props.bar, - (foo, bar) => ({ foo, bar }) + (foo, bar) => ({ foo, bar }), ) // @ts-expect-error @@ -735,12 +734,12 @@ function testCreateSelectorCreator() { function testCreateStructuredSelector() { const oneParamSelector = createStructuredSelector({ foo: (state: StateAB) => state.a, - bar: (state: StateAB) => state.b + bar: (state: StateAB) => state.b, }) const threeParamSelector = createStructuredSelector({ foo: (state: StateAB, c: number, d: string) => state.a, - bar: (state: StateAB, c: number, d: string) => state.b + bar: (state: StateAB, c: number, d: string) => state.b, }) interface RootState { @@ -753,7 +752,7 @@ function testCreateStructuredSelector() { const selector = typedStructuredSelectorCreator({ foo: state => state.foo, - bar: state => +state.foo + bar: state => +state.foo, }) const res1 = selector({ foo: '42', bar: 1 }) @@ -768,15 +767,15 @@ function testCreateStructuredSelector() { typedStructuredSelectorCreator({ // @ts-expect-error - bar: (state: { baz: boolean }) => 1 + bar: (state: { baz: boolean }) => 1, }) typedStructuredSelectorCreator({ - bar: state => state.foo + bar: state => state.foo, }) typedStructuredSelectorCreator({ - baz: state => state.foo + baz: state => state.foo, }) // Test automatic inference of types for createStructuredSelector via overload @@ -786,7 +785,7 @@ function testCreateStructuredSelector() { const selector2 = createStructuredSelector({ foo: FooSelector, - bar: BarSelector + bar: BarSelector, }) const selectorGenerics = createStructuredSelector<{ @@ -794,7 +793,7 @@ function testCreateStructuredSelector() { bar: typeof BarSelector }>({ foo: state => state.foo, - bar: state => +state.foo + bar: state => +state.foo, }) type ExpectedResult = { @@ -809,7 +808,7 @@ function testCreateStructuredSelector() { const resGenerics: ExpectedResult = selectorGenerics( { foo: '42' }, 99, - 'test' + 'test', ) //@ts-expect-error @@ -833,12 +832,12 @@ function testTypedCreateStructuredSelector() { typedStructuredSelectorCreator({ foo: selectFoo, - bar: selectBar + bar: selectBar, }) // @ts-expect-error Because `bar` is missing. typedStructuredSelectorCreator({ - foo: selectFoo + foo: selectFoo, }) // This works @@ -847,7 +846,7 @@ function testTypedCreateStructuredSelector() { bar: typeof selectBar }>({ foo: state => state.foo, - bar: state => +state.foo + bar: state => +state.foo, }) // This also works @@ -856,7 +855,7 @@ function testTypedCreateStructuredSelector() { bar: typeof selectBar }>({ foo: state => state.foo, - bar: state => +state.foo + bar: state => +state.foo, }) // Their types are the same. @@ -870,33 +869,33 @@ function testDynamicArrayArgument() { } const data: ReadonlyArray = [ { val1: 'a', val2: 'aa' }, - { val1: 'b', val2: 'bb' } + { val1: 'b', val2: 'bb' }, ] createSelector( data.map(obj => () => obj.val1), - (...vals) => vals.join(',') + (...vals) => vals.join(','), ) createSelector( data.map(obj => () => obj.val1), // @ts-expect-error - vals => vals.join(',') + vals => vals.join(','), ) createSelector( data.map(obj => () => obj.val1), - (...vals: string[]) => 0 + (...vals: string[]) => 0, ) // @ts-expect-error createSelector( data.map(obj => () => obj.val1), - (...vals: number[]) => 0 + (...vals: number[]) => 0, ) const s = createSelector( data.map(obj => (state: StateA, fld: keyof Elem) => obj[fld]), - (...vals) => vals.join(',') + (...vals) => vals.join(','), ) s({ a: 42 }, 'val1') s({ a: 42 }, 'val2') @@ -916,7 +915,7 @@ function testStructuredSelectorTypeParams() { // Output state should be the same as input, if not provided // @ts-expect-error createStructuredSelector({ - foo: selectFoo + foo: selectFoo, // bar: selectBar, // ^^^ because this is missing, an error is thrown }) @@ -926,7 +925,7 @@ function multiArgMemoize any>( func: F, a: number, b: string, - equalityCheck = referenceEqualityCheck + equalityCheck = referenceEqualityCheck, ): F { // @ts-ignore return () => {} @@ -947,19 +946,19 @@ function multiArgMemoize any>( const createTransactionsSelector = createSelectorCreator( lruMemoize, - collectionsEqual + collectionsEqual, ) const createMultiMemoizeArgSelector = createSelectorCreator( multiArgMemoize, 42, 'abcd', - referenceEqualityCheck + referenceEqualityCheck, ) const select = createMultiMemoizeArgSelector( (state: { foo: string }) => state.foo, - foo => foo + '!' + foo => foo + '!', ) // error is not applicable anymore select.clearCache() @@ -968,13 +967,13 @@ function multiArgMemoize any>( multiArgMemoize, 42, // @ts-expect-error - referenceEqualityCheck + referenceEqualityCheck, ) const groupTransactionsByLabel = lruMemoize( (transactions: Transaction[]) => groupBy(transactions, item => item.transactionId), - collectionsEqual + collectionsEqual, ) } @@ -997,18 +996,18 @@ function issue445() { function generateObject1(str: string): Object1 { return { - str + str, } } function generateObject2(num: number): Object2 { return { - num + num, } } function generateComplexObject( num: number, subObject: Object1, - subObject2: Object2 + subObject2: Object2, ): boolean { return true } @@ -1029,28 +1028,28 @@ function issue445() { // @ts-expect-error const getComplexObjectTest1 = createSelector( [getObject1], - generateComplexObject + generateComplexObject, ) // Does error, but error is really weird and talks about "Object1 is not assignable to type number" // @ts-expect-error const getComplexObjectTest2 = createSelector( [getNumber, getObject1], - generateComplexObject + generateComplexObject, ) // Should error because number can't be null // @ts-expect-error const getComplexObjectTest3 = createSelector( [getNumber, getObject1, getObject2], - generateComplexObject + generateComplexObject, ) // Does error, but error is really weird and talks about "Object1 is not assignable to type number" // @ts-expect-error const getComplexObjectTest4 = createSelector( [getObject1, getNumber, getObject2], - generateComplexObject + generateComplexObject, ) // Verbose selector examples @@ -1058,40 +1057,40 @@ function issue445() { // Errors correctly, says str can't be null const getVerboseObject1 = createSelector([getString], str => // @ts-expect-error - generateObject1(str) + generateObject1(str), ) // Errors correctly, says num can't be null const getVerboseObject2 = createSelector([getNumber], num => // @ts-expect-error - generateObject2(num) + generateObject2(num), ) // Errors correctly const getVerboseComplexObjectTest1 = createSelector([getObject1], obj1 => // @ts-expect-error - generateComplexObject(obj1) + generateComplexObject(obj1), ) // Errors correctly const getVerboseComplexObjectTest2 = createSelector( [getNumber, getObject1], // @ts-expect-error - (num, obj1) => generateComplexObject(num, obj1) + (num, obj1) => generateComplexObject(num, obj1), ) // Errors correctly const getVerboseComplexObjectTest3 = createSelector( [getNumber, getObject1, getObject2], // @ts-expect-error - (num, obj1, obj2) => generateComplexObject(num, obj1, obj2) + (num, obj1, obj2) => generateComplexObject(num, obj1, obj2), ) // Errors correctly const getVerboseComplexObjectTest4 = createSelector( [getObject1, getNumber, getObject2], // @ts-expect-error - (num, obj1, obj2) => generateComplexObject(num, obj1, obj2) + (num, obj1, obj2) => generateComplexObject(num, obj1, obj2), ) } @@ -1100,13 +1099,13 @@ function issue492() { const fooPropSelector = (_: {}, ownProps: { foo: string }) => ownProps.foo const fooBarPropsSelector = ( _: {}, - ownProps: { foo: string; bar: string } + ownProps: { foo: string; bar: string }, ) => [ownProps.foo, ownProps.bar] const combinedSelector = createSelector( fooPropSelector, fooBarPropsSelector, - (foo, fooBar) => fooBar + (foo, fooBar) => fooBar, ) } @@ -1115,7 +1114,7 @@ function customMemoizationOptionTypes() { f: (...args: any[]) => any, a: string, b: number, - c: boolean + c: boolean, ) => { return f } @@ -1124,14 +1123,14 @@ function customMemoizationOptionTypes() { customMemoize, 'a', 42, - true + true, ) // @ts-expect-error const customSelectorCreatorCustomMemoizeMissingArg = createSelectorCreator( customMemoize, 'a', - true + true, ) } @@ -1143,8 +1142,8 @@ function createSelectorConfigOptions() { (a, b) => a + b, { memoize: lruMemoize, - memoizeOptions: (a, b) => a === b - } + memoizeOptions: (a, b) => a === b, + }, ) const lruMemoizeAcceptsFirstArgAsObject = createSelector( @@ -1154,9 +1153,9 @@ function createSelectorConfigOptions() { { memoize: lruMemoize, memoizeOptions: { - equalityCheck: (a, b) => a === b - } - } + equalityCheck: (a, b) => a === b, + }, + }, ) const lruMemoizeAcceptsArgsAsArray = createSelector( @@ -1165,15 +1164,15 @@ function createSelectorConfigOptions() { (a, b) => a + b, { memoize: lruMemoize, - memoizeOptions: [(a, b) => a === b] - } + memoizeOptions: [(a, b) => a === b], + }, ) const customSelectorCreatorMicroMemoize = createSelectorCreator( microMemoize, { - maxSize: 42 - } + maxSize: 42, + }, ) customSelectorCreatorMicroMemoize( @@ -1183,10 +1182,10 @@ function createSelectorConfigOptions() { { memoizeOptions: [ { - maxSize: 42 - } - ] - } + maxSize: 42, + }, + ], + }, ) const customSelectorCreatorMemoizeOne = createSelectorCreator(memoizeOne) @@ -1196,8 +1195,8 @@ function createSelectorConfigOptions() { (state: StateAB) => state.b, (a, b) => a + b, { - memoizeOptions: (a, b) => a === b - } + memoizeOptions: (a, b) => a === b, + }, ) } @@ -1232,7 +1231,7 @@ const withLotsOfInputSelectors = createSelector( (_state: StateA) => 26, (_state: StateA) => 27, (_state: StateA) => 28, - (...args) => args.length + (...args) => args.length, ) type SelectorArray29 = [ @@ -1264,7 +1263,7 @@ type SelectorArray29 = [ (_state: StateA) => 26, (_state: StateA) => 27, (_state: StateA) => 28, - (_state: StateA) => 29 + (_state: StateA) => 29, ] type Results = SelectorResultArray @@ -1285,7 +1284,7 @@ type State = GetStateFromSelectors const selector = createSelector( (state: { foo: string }) => 1, (state: { bar: string }) => 2, - (...args) => 0 + (...args) => 0, ) selector({ foo: '', bar: '' }) // @ts-expect-error @@ -1298,7 +1297,7 @@ type State = GetStateFromSelectors const selector = createSelector( (state: { foo: string }) => 1, (state: { foo: string }) => 2, - (...args) => 0 + (...args) => 0, ) // @ts-expect-error selector({ foo: '', bar: '' }) @@ -1319,7 +1318,7 @@ function testInputSelectorWithUndefinedReturn() { // Make sure the selector type is honored const selector: SelectorType = createSelector( ({ field }: Input) => field, - args => 'test' + args => 'test', ) // even when memoizeOptions are passed @@ -1328,15 +1327,15 @@ function testInputSelectorWithUndefinedReturn() { args => 'test', { memoize: lruMemoize, - memoizeOptions: { maxSize: 42 } - } + memoizeOptions: { maxSize: 42 }, + }, ) // Make sure inference of functions works... const selector3: SelectorType = createSelector(input, result) const selector4: SelectorType = createSelector(input, result, { memoize: lruMemoize, - memoizeOptions: { maxSize: 42 } + memoizeOptions: { maxSize: 42 }, }) } @@ -1381,20 +1380,20 @@ function issue540() { _: StateA, { testNumber }: { testNumber: number }, c: number, - d: string + d: string, ) => testNumber const input2 = ( _: StateA, { testString }: { testString: string }, - c: number | string + c: number | string, ) => testString const input3 = ( _: StateA, { testBoolean }: { testBoolean: boolean }, c: number | string, - d: string + d: string, ) => testBoolean const input4 = (_: StateA, { testString2 }: { testString2: string }) => @@ -1405,7 +1404,7 @@ function issue540() { input2, input3, input4, - (testNumber, testString, testBoolean) => testNumber + testString + (testNumber, testString, testBoolean) => testNumber + testString, ) const state: StateA = { a: 42 } @@ -1413,21 +1412,21 @@ function issue540() { state, { testNumber: 1, testString: '10', testBoolean: true, testString2: 'blah' }, 42, - 'blah' + 'blah', ) // #541 const selectProp1 = createSelector( [ (state: StateA) => state, - (state: StateA, props: { prop1: number }) => props + (state: StateA, props: { prop1: number }) => props, ], - (state, { prop1 }) => [state, prop1] + (state, { prop1 }) => [state, prop1], ) const selectProp2 = createSelector( [selectProp1, (state, props: { prop2: number }) => props], - (state, { prop2 }) => [state, prop2] + (state, { prop2 }) => [state, prop2], ) selectProp1({ a: 42 }, { prop1: 1 }) @@ -1448,12 +1447,12 @@ function issue548() { const isLoading = createSelector( (state: State) => state, (_: State, props: Props) => props.currency, - ({ loading }, currency) => loading + ({ loading }, currency) => loading, ) const mapData = createStructuredSelector({ isLoading, - test2: (state: State) => 42 + test2: (state: State) => 42, }) const result = mapData({ value: null, loading: false }, { currency: 'EUR' }) @@ -1463,7 +1462,7 @@ function issue550() { const some = createSelector( (a: number) => a, (_a: number, b: number) => b, - (a, b) => a + b + (a, b) => a + b, ) const test = some(1, 2) @@ -1473,7 +1472,7 @@ function rtkIssue1750() { const slice = createSlice({ name: 'test', initialState: 0, - reducers: {} + reducers: {}, }) interface Pokemon { @@ -1486,25 +1485,25 @@ function rtkIssue1750() { baseQuery: fetchBaseQuery({ baseUrl: 'https://pokeapi.co/api/v2/' }), endpoints: builder => ({ getPokemonByName: builder.query({ - query: name => `pokemon/${name}` - }) - }) + query: name => `pokemon/${name}`, + }), + }), }) const store = configureStore({ reducer: { test: slice.reducer, - [pokemonApi.reducerPath]: pokemonApi.reducer + [pokemonApi.reducerPath]: pokemonApi.reducer, }, middleware: getDefaultMiddleware => - getDefaultMiddleware().concat(pokemonApi.middleware) + getDefaultMiddleware().concat(pokemonApi.middleware), }) type RootState = ReturnType const selectTest = createSelector( (state: RootState) => state.test, - test => test + test => test, ) const useAppSelector: TypedUseSelectorHook = useSelector @@ -1550,7 +1549,7 @@ function issue554a() { const initialState: State = { foo: 'This is Foo', - bar: 1 + bar: 1, } const getFoo = (state: State) => { @@ -1579,7 +1578,7 @@ function issue554a() { firstInput, (foo, bar, first) => { return `${foo} => ${bar} || ${first}` - } + }, ) complexOne(initialState, 'first') @@ -1590,7 +1589,7 @@ function issue554a() { secondInput, (foo, bar, first, second) => { return `${foo} => ${bar} || ${first} and ${second}` - } + }, ) // TS should complain since 'second' should be `number` // @ts-expect-error @@ -1606,7 +1605,7 @@ function issue554b() { const selectTest = createSelector( (state: State, numberA?: number) => numberA, (state: State) => state.counter2, - (numberA, counter2) => (numberA ? numberA + counter2 : counter2) + (numberA, counter2) => (numberA ? numberA + counter2 : counter2), ) type selectTestParams = Parameters @@ -1625,7 +1624,7 @@ function issue554c() { const selectTest = createSelector( (state: State, numberA?: number) => numberA, // `numberA` is optional (state: State) => state.counter2, - (numberA, counter2) => (numberA ? numberA + counter2 : counter2) + (numberA, counter2) => (numberA ? numberA + counter2 : counter2), ) // @ts-expect-error @@ -1634,7 +1633,7 @@ function issue554c() { const selectTest2 = createSelector( (state: State, numberA: number) => numberA, // `numberA` is not optional anymore (state: State) => state.counter2, - (numberA, counter2) => (numberA ? numberA + counter2 : counter2) + (numberA, counter2) => (numberA ? numberA + counter2 : counter2), ) // @ts-expect-error @@ -1652,19 +1651,19 @@ function issue555() { const someSelector1 = createSelector( (state: IReduxState, param: 'x' | 'y' | undefined) => param !== undefined ? state.ui[param] : null, - (a: string | null) => a + (a: string | null) => a, ) const someSelector2 = createSelector( (state: IReduxState, param?: 'x' | 'y') => param !== undefined ? state.ui[param] : null, - (a: string | null) => a + (a: string | null) => a, ) const someSelector3 = createSelector( (state: IReduxState, param: 'x' | 'y' | null) => param !== null ? state.ui[param] : null, - (a: string | null) => a + (a: string | null) => a, ) const state = { ui: { x: '1', y: '2' } } @@ -1684,14 +1683,14 @@ function testCreateStructuredSelectorNew() { const state: State = { todos: [ { id: 0, completed: false }, - { id: 1, completed: false } - ] + { id: 1, completed: false }, + ], } const selectorDefaultParametric = createSelector( (state: State, id: number) => id, (state: State) => state.todos, - (id, todos) => todos.filter(todo => todo.id === id) + (id, todos) => todos.filter(todo => todo.id === id), ) const multiArgsStructuredSelector = createStructuredSelector( { @@ -1700,22 +1699,22 @@ function testCreateStructuredSelectorNew() { selectedCompletedTodos: ( state: State, id: number, - isCompleted: boolean - ) => state.todos.filter(({ completed }) => completed === isCompleted) + isCompleted: boolean, + ) => state.todos.filter(({ completed }) => completed === isCompleted), }, - createSelectorCreator({ memoize: microMemoize, argsMemoize: microMemoize }) + createSelectorCreator({ memoize: microMemoize, argsMemoize: microMemoize }), ) multiArgsStructuredSelector.resultFunc( [{ id: 2, completed: true }], { id: 0, completed: false }, - [{ id: 0, completed: false }] + [{ id: 0, completed: false }], ).selectedCompletedTodos multiArgsStructuredSelector.memoizedResultFunc( [{ id: 2, completed: true }], { id: 0, completed: false }, - [{ id: 0, completed: false }] + [{ id: 0, completed: false }], ).selectedCompletedTodos multiArgsStructuredSelector.memoizedResultFunc.cache @@ -1730,7 +1729,7 @@ function testCreateStructuredSelectorNew() { [ (state: State) => State['todos'], (state: State, id: number) => State['todos'][number], - (state: State, id: number, isCompleted: boolean) => State['todos'] + (state: State, id: number, isCompleted: boolean) => State['todos'], ] >(multiArgsStructuredSelector.dependencies) // @ts-expect-error Wrong number of arguments. diff --git a/typescript_test/typesTestUtils.ts b/typescript_test/typesTestUtils.ts index f0ca45a20..ea3ace8a7 100644 --- a/typescript_test/typesTestUtils.ts +++ b/typescript_test/typesTestUtils.ts @@ -1,29 +1,28 @@ -export function expectType(t: T): T { - return t -} - -export declare type IsAny = true | false extends ( - T extends never ? true : false -) - ? True - : False - -export declare type IsUnknown = unknown extends T - ? IsAny - : False - -type Equals = IsAny< - T, - never, - IsAny -> - -export type IsEqual = (() => G extends A ? 1 : 2) extends < - G ->() => G extends B ? 1 : 2 - ? true - : false - -export function expectExactType(t: T) { - return (u: U & Equals) => {} -} +export function expectType(t: T): T { + return t +} + +export declare type IsAny = true | false extends ( + T extends never ? true : false +) + ? True + : False + +export declare type IsUnknown = unknown extends T + ? IsAny + : False + +type Equals = IsAny< + T, + never, + IsAny +> + +export type IsEqual = + (() => G extends A ? 1 : 2) extends () => G extends B ? 1 : 2 + ? true + : false + +export function expectExactType(t: T) { + return (u: U & Equals) => {} +} diff --git a/vitest.config.mts b/vitest.config.mts index b45318ec5..f9a4ba52e 100644 --- a/vitest.config.mts +++ b/vitest.config.mts @@ -17,7 +17,7 @@ export default defineConfig({ reselect: path.join(__dirname, 'src/index.ts'), // @remap-prod-remove-line // this mapping is disabled as we want `dist` imports in the tests only to be used for "type-only" imports which don't play a role for jest - '@internal': path.join(__dirname, 'src') - } - } + '@internal': path.join(__dirname, 'src'), + }, + }, }) diff --git a/website/babel.config.js b/website/babel.config.js index 14e5baf05..675264818 100644 --- a/website/babel.config.js +++ b/website/babel.config.js @@ -1,3 +1,3 @@ module.exports = { - presets: [require.resolve('@docusaurus/core/lib/babel/preset')] + presets: [require.resolve('@docusaurus/core/lib/babel/preset')], } diff --git a/website/compileExamples.ts b/website/compileExamples.ts index 4978db244..32fd15ba7 100644 --- a/website/compileExamples.ts +++ b/website/compileExamples.ts @@ -4,7 +4,7 @@ import { mkdirSync, readFileSync, readdirSync, - writeFileSync + writeFileSync, } from 'node:fs' import path from 'node:path' import ts from 'typescript' @@ -16,7 +16,7 @@ const preferredTags = { NEW_LINE_TAG: ['N', 'n'], SPACES_TAG: ['S', 's'], SPACES_BEFORE_COLON_TAG: ['C', 'c'], - SAME_LINE_ELSE_TAG: ['E', 'e'] + SAME_LINE_ELSE_TAG: ['E', 'e'], } type PreferredTags = typeof preferredTags @@ -48,7 +48,7 @@ class ParsedFileMetadata { public serialize() { return `; /*${ParsedFileMetadata.FILE_METADATA_TAG}${JSON.stringify( - this.metadata + this.metadata, )}${ParsedFileMetadata.FILE_METADATA_TAG}*/\n` } @@ -57,7 +57,7 @@ class ParsedFileMetadata { const endTagRegex = `${ParsedFileMetadata.FILE_METADATA_TAG}\\*\\/\\r?\\n?` const metadataMatch = fileContents.match( - new RegExp(`${startTagRegex}([\\s\\S]*?)${endTagRegex}`) + new RegExp(`${startTagRegex}([\\s\\S]*?)${endTagRegex}`), ) if (metadataMatch === null) { @@ -96,13 +96,13 @@ const restoreWhitespace = (contents: string) => { NEW_LINE_TAG, SPACES_TAG, SPACES_BEFORE_COLON_TAG, - SAME_LINE_ELSE_TAG + SAME_LINE_ELSE_TAG, } = metadata if (options.preserveNewLines) { contents = contents.replace( new RegExp(`\\/\\*${NEW_LINE_TAG}\\*\\/`, 'g'), - '' + '', ) } @@ -112,7 +112,7 @@ const restoreWhitespace = (contents: string) => { (match, group1: string) => { const spacesCount = Number(group1) return `${' '.repeat(spacesCount)}:` - } + }, ) if (options.collapseSpacesBeforeRemovedColons) { @@ -121,13 +121,13 @@ const restoreWhitespace = (contents: string) => { ' ?\\/\\*' + SPACES_BEFORE_COLON_TAG + '([0-9]+)\\*\\/(?=[,;\\)\\} \\t\\r\\n])', - 'g' + 'g', ), - '' + '', ) contents = contents.replace( new RegExp(` ?\\/\\*${SPACES_BEFORE_COLON_TAG}([0-9]+)\\*\\/`, 'g'), - ' ' + ' ', ) } else { contents = contents.replace( @@ -135,7 +135,7 @@ const restoreWhitespace = (contents: string) => { (match, group1: string) => { const spacesCount = Number(group1) return ' '.repeat(spacesCount) - } + }, ) } } @@ -146,7 +146,7 @@ const restoreWhitespace = (contents: string) => { (match, group1: string) => { const spacesCount = Number(group1) return ' '.repeat(spacesCount - 2) - } + }, ) } @@ -154,12 +154,12 @@ const restoreWhitespace = (contents: string) => { return contents.replace( new RegExp( `\\} \\/\\*${SAME_LINE_ELSE_TAG}([0-9]+)\\*\\/\\r?\\n[ \\t]*else`, - 'g' + 'g', ), (match, group1: string) => { const spacesCount = Number(group1) return `}${' '.repeat(spacesCount)}else` - } + }, ) } @@ -196,7 +196,7 @@ class UnusedTagsFinder { constructor( public fileContents: string, - public options: PreserveTypescriptWhitespaceOptions + public options: PreserveTypescriptWhitespaceOptions, ) { this.checkedSimpleTags = new Set() this.checkedTagsWithCount = new Set() @@ -243,7 +243,7 @@ const options = { preserveSpacesBeforeColons: true, collapseSpacesBeforeRemovedColons: true, preserveSameLineElse: true, - showDebugOutput: false + showDebugOutput: false, } const rebuildCodeFromBlocks = (blocks: Block[]) => { @@ -255,12 +255,12 @@ const stringOrCommentEnd = { '"': /(? { const blocks: Block[] = [] @@ -312,19 +312,19 @@ const saveWhitespace = (file: string) => { const unusedTagsFinder = new UnusedTagsFinder(file, options) const NEW_LINE_TAG = unusedTagsFinder.findUnusedTag( preferredTags.NEW_LINE_TAG, - false + false, ) const SPACES_TAG = unusedTagsFinder.findUnusedTag( preferredTags.SPACES_TAG, - true + true, ) const SPACES_BEFORE_COLON_TAG = unusedTagsFinder.findUnusedTag( preferredTags.SPACES_BEFORE_COLON_TAG, - true + true, ) const SAME_LINE_ELSE_TAG = unusedTagsFinder.findUnusedTag( preferredTags.SAME_LINE_ELSE_TAG, - true + true, ) const NEW_LINE_REPLACEMENT = `/*${NEW_LINE_TAG}*/$1` @@ -345,12 +345,12 @@ const saveWhitespace = (file: string) => { block.code = block.code.replace( isFileStart ? /(?<=[^ \n])( +)(?![ :])/g : /(?<=^|[^ \n])( +)(?![ :])/g, - (match, group1: string) => ` /*${SPACES_TAG}${group1.length}*/ ` + (match, group1: string) => ` /*${SPACES_TAG}${group1.length}*/ `, ) block.code = block.code.replace( isFileStart ? /(?<=(?:^|\n)[ \t]*)(\r?\n)/g : /(?<=\n[ \t]*)(\r?\n)/g, - NEW_LINE_REPLACEMENT + NEW_LINE_REPLACEMENT, ) isFileStart = false @@ -363,7 +363,7 @@ const saveWhitespace = (file: string) => { NEW_LINE_TAG, SPACES_TAG, SPACES_BEFORE_COLON_TAG, - SAME_LINE_ELSE_TAG + SAME_LINE_ELSE_TAG, }) return metadataObj.serialize() + file @@ -409,8 +409,8 @@ const compileTSFile = (filePath: string, tsconfigPath?: string) => { const result = ts.transpileModule(savedWhitespaceContents, { compilerOptions: { ...config.compilerOptions, - jsx - } + jsx, + }, }) const { outDir } = config.compilerOptions const tsconfigDirectory = path.dirname(tsconfigPath) @@ -419,7 +419,7 @@ const compileTSFile = (filePath: string, tsconfigPath?: string) => { outDir, ...inputFileDirectory .split(path.sep) - .slice(tsconfigDirectory.split(path.sep).length) + .slice(tsconfigDirectory.split(path.sep).length), ) const restoredWhitespaceContents = restoreWhitespace(result.outputText) @@ -453,5 +453,5 @@ const compileTSWithWhitespace = (directory: string, tsconfigPath: string) => { compileTSWithWhitespace( EXAMPLES_DIRECTORY, - path.join(EXAMPLES_DIRECTORY, 'tsconfig.json') + path.join(EXAMPLES_DIRECTORY, 'tsconfig.json'), ) diff --git a/website/docs/FAQ.mdx b/website/docs/FAQ.mdx index ab98c197e..a947a5da7 100644 --- a/website/docs/FAQ.mdx +++ b/website/docs/FAQ.mdx @@ -48,7 +48,7 @@ export interface RootState { const selectAlertsByType = createSelector( [ (state: RootState) => state.alerts, - (state: RootState, type: string) => type + (state: RootState, type: string) => type, ], (alerts, type) => alerts.filter(todo => todo.type === type), { @@ -60,9 +60,9 @@ const selectAlertsByType = createSelector( console.log('Changed argument:', a, 'to', b) } return a === b - } - } - } + }, + }, + }, ) ``` @@ -84,9 +84,9 @@ const selectAlertsByType = createSelector( console.log('Changed argument:', a, 'to', b) } return a === b - } - } - } + }, + }, + }, ) ``` @@ -108,7 +108,7 @@ const selectTodosByCategory = createSelector( (state: RootState) => state.todos, // Extract the second argument to pass it on (state: RootState, category: string) => category, - (todos, category) => todos.filter(t => t.category === category) + (todos, category) => todos.filter(t => t.category === category), ) ``` @@ -141,11 +141,11 @@ const selectAvailableItems = createSelector( // Second input selector forwards the category argument (state: RootState, category: string) => category, // Third input selector forwards the ID argument - (state: RootState, category: string, id: number) => id + (state: RootState, category: string, id: number) => id, ], // Output selector uses the extracted items, category, and ID (items, category, id) => - items.filter(item => item.category === category && item.id !== id) + items.filter(item => item.category === category && item.id !== id), ) ``` @@ -175,12 +175,12 @@ const selectItemId = (state: RootState, itemId: number) => itemId // expects an object as the second argument const selectVendorName = ( state: RootState, - vendor: { id: number; name: string } + vendor: { id: number; name: string }, ) => vendor.name const selectItemById = createSelector( [selectItems, selectItemId, selectVendorName], - (items, itemId, vendorName) => items[itemId] + (items, itemId, vendorName) => items[itemId], ) ``` @@ -215,19 +215,19 @@ interface RootState { const state: RootState = { todos: [ { id: 0, completed: false }, - { id: 1, completed: true } + { id: 1, completed: true }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } // With `Vitest` or `Jest` test('selector unit test', () => { const selectTodoIds = createSelector( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const firstResult = selectTodoIds(state) const secondResult = selectTodoIds(state) @@ -248,7 +248,7 @@ test('selector unit test', () => { test('selector unit test', () => { const selectTodoIds = createSelector( [(state: RootState) => state.todos], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) const firstResult = selectTodoIds(state) const secondResult = selectTodoIds(state) @@ -276,18 +276,18 @@ import { expect, test } from 'vitest' const state = { todos: [ { id: 0, completed: false }, - { id: 1, completed: true } + { id: 1, completed: true }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } // With `Vitest` or `Jest` test('selector unit test', () => { const selectTodoIds = createSelector([state => state.todos], todos => - todos.map(({ id }) => id) + todos.map(({ id }) => id), ) const firstResult = selectTodoIds(state) const secondResult = selectTodoIds(state) @@ -307,7 +307,7 @@ test('selector unit test', () => { // With `Chai` test('selector unit test', () => { const selectTodoIds = createSelector([state => state.todos], todos => - todos.map(({ id }) => id) + todos.map(({ id }) => id), ) const firstResult = selectTodoIds(state) const secondResult = selectTodoIds(state) @@ -354,7 +354,7 @@ Selectors that take arguments are commonly used inside of React-Redux's `useSele ```ts function TodosList({ category }) { const filteredTodos = useSelector(state => - selectTodosByCategory(state, category) + selectTodosByCategory(state, category), ) } ``` @@ -380,9 +380,9 @@ export const currySelector = < State, Result, Params extends readonly any[], - AdditionalFields + AdditionalFields, >( - selector: ((state: State, ...args: Params) => Result) & AdditionalFields + selector: ((state: State, ...args: Params) => Result) & AdditionalFields, ) => { const curriedSelector = (...args: Params) => { return (state: State) => { @@ -395,8 +395,8 @@ export const currySelector = < const selectTodoByIdCurried = currySelector( createSelector( [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id) - ) + (todos, id) => todos.find(todo => todo.id === id), + ), ) ``` @@ -417,8 +417,8 @@ export const currySelector = selector => { const selectTodoByIdCurried = currySelector( createSelector([state => state.todos, (state, id) => id], (todos, id) => - todos.find(todo => todo.id === id) - ) + todos.find(todo => todo.id === id), + ), ) ``` @@ -449,7 +449,7 @@ export const createCurriedSelector = < InputSelectors extends SelectorArray, Result, OverrideMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - OverrideArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + OverrideArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, >( ...args: Parameters< typeof createSelector< @@ -486,7 +486,7 @@ This: ```ts const selectTodoById = createSelector( [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id) + (todos, id) => todos.find(todo => todo.id === id), ) selectTodoById(state, 0) @@ -543,31 +543,31 @@ const state: RootState = { id: 0, completed: false, title: 'Figure out if plants are really plotting world domination.', - description: 'They may be.' + description: 'They may be.', }, { id: 1, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } + description: 'Just do it', + }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } const selectTodoById = createSelector( [(state: RootState) => state.todos, (state: RootState, id: number) => id], - (todos, id) => todos.find(todo => todo.id === id) + (todos, id) => todos.find(todo => todo.id === id), ) export const createParametricSelectorHook = < Result, - Params extends readonly unknown[] + Params extends readonly unknown[], >( - selector: (state: RootState, ...params: Params) => Result + selector: (state: RootState, ...params: Params) => Result, ) => { return (...args: Params) => { return useSelector((state: RootState) => selector(state, ...args)) @@ -590,24 +590,24 @@ const state = { id: 0, completed: false, title: 'Figure out if plants are really plotting world domination.', - description: 'They may be.' + description: 'They may be.', }, { id: 1, completed: true, title: 'Practice telekinesis for 15 minutes', - description: 'Just do it' - } + description: 'Just do it', + }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } const selectTodoById = createSelector( [state => state.todos, (state, id) => id], - (todos, id) => todos.find(todo => todo.id === id) + (todos, id) => todos.find(todo => todo.id === id), ) export const createParametricSelectorHook = selector => { @@ -679,7 +679,7 @@ import type { OutputSelector, Selector, SelectorArray, - UnknownMemoizer + UnknownMemoizer, } from 'reselect' import { createSelector } from 'reselect' @@ -691,12 +691,12 @@ interface RootState { export type TypedCreateSelector< State, MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, - ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize + ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize, > = < InputSelectors extends readonly Selector[], Result, OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction, - OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction + OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction, >( ...createSelectorArgs: Parameters< typeof createSelector< @@ -746,7 +746,7 @@ const identity = any>(func: Func) => func const createNonMemoizedSelector = createSelectorCreator({ memoize: identity, - argsMemoize: identity + argsMemoize: identity, }) ``` @@ -760,7 +760,7 @@ const identity = func => func const createNonMemoizedSelector = createSelectorCreator({ memoize: identity, - argsMemoize: identity + argsMemoize: identity, }) ``` diff --git a/website/docs/api/createSelector.mdx b/website/docs/api/createSelector.mdx index ced43047e..57256cb49 100644 --- a/website/docs/api/createSelector.mdx +++ b/website/docs/api/createSelector.mdx @@ -24,12 +24,12 @@ const selectTodosByCategory = createSelector( [ // Pass input selectors with typed arguments (state: RootState) => state.todos, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], // Extracted values are passed to the result function for recalculation (todos, category) => { return todos.filter(t => t.category === category) - } + }, ) ``` @@ -102,10 +102,10 @@ const selectTodoIds = createAppSelector( [ // Type of `state` is set to `RootState`, no need to manually set the type // highlight-start - state => state.todos + state => state.todos, // highlight-end ], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) ``` @@ -121,10 +121,10 @@ const selectTodoIds = createAppSelector( [ // Type of `state` is set to `RootState`, no need to manually set the type // highlight-start - state => state.todos + state => state.todos, // highlight-end ], - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), ) ``` @@ -182,7 +182,7 @@ const selectTodoIds = createAppSelector( // ❌ Known limitation: Parameter types are not inferred in this scenario // so you will have to manually annotate them. // highlight-start - (todos: Todo[]) => todos.map(({ id }) => id) + (todos: Todo[]) => todos.map(({ id }) => id), // highlight-end ) ``` @@ -201,7 +201,7 @@ const selectTodoIds = createAppSelector( // ❌ Known limitation: Parameter types are not inferred in this scenario // so you will have to manually annotate them. // highlight-start - todos => todos.map(({ id }) => id) + todos => todos.map(({ id }) => id), // highlight-end ) ``` @@ -244,26 +244,26 @@ export const createAppSelector = createSelectorCreator({ memoizeOptions: { maxSize: 10, equalityCheck: shallowEqual, - resultEqualityCheck: shallowEqual + resultEqualityCheck: shallowEqual, }, argsMemoizeOptions: { isEqual: shallowEqual, - maxSize: 10 + maxSize: 10, }, devModeChecks: { identityFunctionCheck: 'never', - inputStabilityCheck: 'always' - } + inputStabilityCheck: 'always', + }, }).withTypes() const selectReadAlerts = createAppSelector( [ // Type of `state` is set to `RootState`, no need to manually set the type // highlight-start - state => state.alerts + state => state.alerts, // highlight-end ], - alerts => alerts.filter(({ read }) => read) + alerts => alerts.filter(({ read }) => read), ) ``` @@ -281,26 +281,26 @@ export const createAppSelector = createSelectorCreator({ memoizeOptions: { maxSize: 10, equalityCheck: shallowEqual, - resultEqualityCheck: shallowEqual + resultEqualityCheck: shallowEqual, }, argsMemoizeOptions: { isEqual: shallowEqual, - maxSize: 10 + maxSize: 10, }, devModeChecks: { identityFunctionCheck: 'never', - inputStabilityCheck: 'always' - } + inputStabilityCheck: 'always', + }, }).withTypes() const selectReadAlerts = createAppSelector( [ // Type of `state` is set to `RootState`, no need to manually set the type // highlight-start - state => state.alerts + state => state.alerts, // highlight-end ], - alerts => alerts.filter(({ read }) => read) + alerts => alerts.filter(({ read }) => read), ) ``` diff --git a/website/docs/api/createSelectorCreator.mdx b/website/docs/api/createSelectorCreator.mdx index 65908b1e9..71b2299dc 100644 --- a/website/docs/api/createSelectorCreator.mdx +++ b/website/docs/api/createSelectorCreator.mdx @@ -50,16 +50,16 @@ const customCreateSelector = createSelectorCreator({ memoize: customMemoize, // Function to be used to memoize `resultFunc` memoizeOptions: [memoizeOption1, memoizeOption2], // Options passed to `customMemoize` as the second argument onwards argsMemoize: customArgsMemoize, // Function to be used to memoize the selector's arguments - argsMemoizeOptions: [argsMemoizeOption1, argsMemoizeOption2] // Options passed to `customArgsMemoize` as the second argument onwards + argsMemoizeOptions: [argsMemoizeOption1, argsMemoizeOption2], // Options passed to `customArgsMemoize` as the second argument onwards }) const customSelector = customCreateSelector( [inputSelector1, inputSelector2], - resultFunc // `resultFunc` will be passed as the first argument to `customMemoize` + resultFunc, // `resultFunc` will be passed as the first argument to `customMemoize` ) customSelector( - ...selectorArgs // Will be memoized by `customArgsMemoize` + ...selectorArgs, // Will be memoized by `customArgsMemoize` ) ``` @@ -76,12 +76,12 @@ const customSelectorCreator = createSelectorCreator( customMemoize, // Function to be used to memoize `resultFunc` option1, // `option1` will be passed as second argument to `customMemoize` option2, // `option2` will be passed as third argument to `customMemoize` - option3 // `option3` will be passed as fourth argument to `customMemoize` + option3, // `option3` will be passed as fourth argument to `customMemoize` ) const customSelector = customSelectorCreator( [inputSelector1, inputSelector2], - resultFunc // `resultFunc` will be passed as first argument to `customMemoize` + resultFunc, // `resultFunc` will be passed as first argument to `customMemoize` ) ``` @@ -105,7 +105,7 @@ const createDeepEqualSelector = createSelectorCreator(lruMemoize, isEqual) // use the new "selector creator" to create a selector const selectSum = createDeepEqualSelector( [state => state.values.filter(val => val < 5)], - values => values.reduce((acc, val) => acc + val, 0) + values => values.reduce((acc, val) => acc + val, 0), ) ``` @@ -122,6 +122,6 @@ const customSelectorCreator = createSelectorCreator(memoize, hashFn) const selector = customSelectorCreator( [state => state.a, state => state.b], - (a, b) => a + b + (a, b) => a + b, ) ``` diff --git a/website/docs/api/createStructuredSelector.mdx b/website/docs/api/createStructuredSelector.mdx index 7eac93bc0..5d3403409 100644 --- a/website/docs/api/createStructuredSelector.mdx +++ b/website/docs/api/createStructuredSelector.mdx @@ -66,9 +66,9 @@ export const structuredSelector = createStructuredSelector( { todos: (state: RootState) => state.todos, alerts: (state: RootState) => state.alerts, - todoById: (state: RootState, id: number) => state.todos[id] + todoById: (state: RootState, id: number) => state.todos[id], }, - createSelector + createSelector, ) // Is essentially the same as this: @@ -76,15 +76,15 @@ export const selector = createSelector( [ (state: RootState) => state.todos, (state: RootState) => state.alerts, - (state: RootState, id: number) => state.todos[id] + (state: RootState, id: number) => state.todos[id], ], (todos, alerts, todoById) => { return { todos, alerts, - todoById + todoById, } - } + }, ) ``` @@ -99,9 +99,9 @@ export const structuredSelector = createStructuredSelector( { todos: state => state.todos, alerts: state => state.alerts, - todoById: (state, id) => state.todos[id] + todoById: (state, id) => state.todos[id], }, - createSelector + createSelector, ) // Is essentially the same as this: @@ -111,9 +111,9 @@ export const selector = createSelector( return { todos, alerts, - todoById + todoById, } - } + }, ) ``` @@ -147,7 +147,7 @@ interface Props { const MyComponent: FC = ({ id }) => { const { todos, alerts, todoById } = useSelector((state: RootState) => - structuredSelector(state, id) + structuredSelector(state, id), ) return ( @@ -175,7 +175,7 @@ import { useSelector } from 'react-redux' const MyComponent = ({ id }) => { const { todos, alerts, todoById } = useSelector(state => - structuredSelector(state, id) + structuredSelector(state, id), ) return ( @@ -209,7 +209,7 @@ const selectB = state => state.b // is simply building an object from the input selectors const structuredSelector = createSelector(selectA, selectB, (a, b) => ({ a, - b + b, })) const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 } @@ -223,7 +223,7 @@ const selectB = state => state.b const structuredSelector = createStructuredSelector({ x: selectA, - y: selectB + y: selectB, }) const result = structuredSelector({ a: 1, b: 2 }) // will produce { x: 1, y: 2 } @@ -235,12 +235,12 @@ Structured selectors can be nested: const nestedSelector = createStructuredSelector({ subA: createStructuredSelector({ selectorA, - selectorB + selectorB, }), subB: createStructuredSelector({ selectorC, - selectorD - }) + selectorD, + }), }) ``` @@ -278,7 +278,7 @@ const structuredAppSelector = createStructuredAppSelector({ todos: state => state.todos, // highlight-end alerts: state => state.alerts, - todoById: (state, id: number) => state.todos[id] + todoById: (state, id: number) => state.todos[id], }) ``` @@ -296,7 +296,7 @@ const structuredAppSelector = createStructuredAppSelector({ todos: state => state.todos, // highlight-end alerts: state => state.alerts, - todoById: (state, id) => state.todos[id] + todoById: (state, id) => state.todos[id], }) ``` diff --git a/website/docs/api/development-only-stability-checks.mdx b/website/docs/api/development-only-stability-checks.mdx index b6b0935f7..f24f913ac 100644 --- a/website/docs/api/development-only-stability-checks.mdx +++ b/website/docs/api/development-only-stability-checks.mdx @@ -94,11 +94,11 @@ const selectCompletedTodosLength = createSelector( // ❌ Incorrect Use Case: This input selector will not be // memoized properly since it always returns a new reference. (state: RootState) => - state.todos.filter(({ completed }) => completed === true) + state.todos.filter(({ completed }) => completed === true), ], completedTodos => completedTodos.length, // Will override the global setting. - { devModeChecks: { inputStabilityCheck: 'always' } } + { devModeChecks: { inputStabilityCheck: 'always' } }, ) ``` @@ -113,11 +113,11 @@ const selectCompletedTodosLength = createSelector( [ // ❌ Incorrect Use Case: This input selector will not be // memoized properly since it always returns a new reference. - state => state.todos.filter(({ completed }) => completed === true) + state => state.todos.filter(({ completed }) => completed === true), ], completedTodos => completedTodos.length, // Will override the global setting. - { devModeChecks: { inputStabilityCheck: 'always' } } + { devModeChecks: { inputStabilityCheck: 'always' } }, ) ``` @@ -150,7 +150,7 @@ const brokenSelector = createSelector( // ✔️ GOOD: Contains extraction logic. [(state: RootState) => state.todos], // ❌ BAD: Does not contain transformation logic. - todos => todos + todos => todos, ) ``` @@ -215,7 +215,7 @@ const selectTodos = createSelector( // ❌ BAD: Does not contain transformation logic. todos => todos, // Will override the global setting. - { devModeChecks: { identityFunctionCheck: 'always' } } + { devModeChecks: { identityFunctionCheck: 'always' } }, ) ``` @@ -232,7 +232,7 @@ const selectTodos = createSelector( // ❌ BAD: Does not contain transformation logic. todos => todos, // Will override the global setting. - { devModeChecks: { identityFunctionCheck: 'always' } } + { devModeChecks: { identityFunctionCheck: 'always' } }, ) ``` diff --git a/website/docs/api/lruMemoize.mdx b/website/docs/api/lruMemoize.mdx index 3d8df1f34..509c80400 100644 --- a/website/docs/api/lruMemoize.mdx +++ b/website/docs/api/lruMemoize.mdx @@ -128,15 +128,15 @@ const selectTodoIds = createSelector( memoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 + maxSize: 10, }, argsMemoize: lruMemoize, argsMemoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 - } - } + maxSize: 10, + }, + }, ) ``` @@ -155,15 +155,15 @@ const selectTodoIds = createSelector( memoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 + maxSize: 10, }, argsMemoize: lruMemoize, argsMemoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 - } - } + maxSize: 10, + }, + }, ) ``` @@ -204,19 +204,19 @@ const createSelectorShallowEqual = createSelectorCreator({ memoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 + maxSize: 10, }, argsMemoize: lruMemoize, argsMemoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 - } + maxSize: 10, + }, }) const selectTodoIds = createSelectorShallowEqual( [(state: RootState) => state.todos], - todos => todos.map(todo => todo.id) + todos => todos.map(todo => todo.id), ) ``` @@ -232,19 +232,19 @@ const createSelectorShallowEqual = createSelectorCreator({ memoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 + maxSize: 10, }, argsMemoize: lruMemoize, argsMemoizeOptions: { equalityCheck: shallowEqual, resultEqualityCheck: shallowEqual, - maxSize: 10 - } + maxSize: 10, + }, }) const selectTodoIds = createSelectorShallowEqual( [state => state.todos], - todos => todos.map(todo => todo.id) + todos => todos.map(todo => todo.id), ) ``` diff --git a/website/docs/api/unstable_autotrackMemoize.mdx b/website/docs/api/unstable_autotrackMemoize.mdx index 2d46e5139..6abcd98bd 100644 --- a/website/docs/api/unstable_autotrackMemoize.mdx +++ b/website/docs/api/unstable_autotrackMemoize.mdx @@ -84,7 +84,7 @@ export interface RootState { const selectTodoIds = createSelector( [(state: RootState) => state.todos], todos => todos.map(todo => todo.id), - { memoize: unstable_autotrackMemoize } + { memoize: unstable_autotrackMemoize }, ) ``` @@ -97,7 +97,7 @@ import { createSelector, unstable_autotrackMemoize } from 'reselect' const selectTodoIds = createSelector( [state => state.todos], todos => todos.map(todo => todo.id), - { memoize: unstable_autotrackMemoize } + { memoize: unstable_autotrackMemoize }, ) ``` @@ -124,12 +124,12 @@ import { createSelectorCreator, unstable_autotrackMemoize } from 'reselect' import type { RootState } from './usingWithCreateSelector' const createSelectorAutotrack = createSelectorCreator({ - memoize: unstable_autotrackMemoize + memoize: unstable_autotrackMemoize, }) const selectTodoIds = createSelectorAutotrack( [(state: RootState) => state.todos], - todos => todos.map(todo => todo.id) + todos => todos.map(todo => todo.id), ) ``` @@ -140,11 +140,11 @@ const selectTodoIds = createSelectorAutotrack( import { createSelectorCreator, unstable_autotrackMemoize } from 'reselect' const createSelectorAutotrack = createSelectorCreator({ - memoize: unstable_autotrackMemoize + memoize: unstable_autotrackMemoize, }) const selectTodoIds = createSelectorAutotrack([state => state.todos], todos => - todos.map(todo => todo.id) + todos.map(todo => todo.id), ) ``` diff --git a/website/docs/api/weakMapMemoize.mdx b/website/docs/api/weakMapMemoize.mdx index 030493ee4..a051c73d5 100644 --- a/website/docs/api/weakMapMemoize.mdx +++ b/website/docs/api/weakMapMemoize.mdx @@ -64,16 +64,16 @@ const state: RootState = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], - (items, category) => items.filter(item => item.category === category) + (items, category) => items.filter(item => item.category === category), ) selectItemsByCategory(state, 'Electronics') // Selector runs @@ -93,13 +93,13 @@ const state = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( [state => state.items, (state, category) => category], - (items, category) => items.filter(item => item.category === category) + (items, category) => items.filter(item => item.category === category), ) selectItemsByCategory(state, 'Electronics') // Selector runs @@ -135,15 +135,15 @@ import type { RootState } from './cacheSizeProblem' const selectItemsByCategory = createSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], (items, category) => items.filter(item => item.category === category), { memoize: lruMemoize, memoizeOptions: { - maxSize: 10 - } - } + maxSize: 10, + }, + }, ) ``` @@ -159,9 +159,9 @@ const selectItemsByCategory = createSelector( { memoize: lruMemoize, memoizeOptions: { - maxSize: 10 - } - } + maxSize: 10, + }, + }, ) ``` @@ -194,7 +194,7 @@ import type { RootState } from './cacheSizeProblem' const makeSelectItemsByCategory = (category: string) => createSelector([(state: RootState) => state.items], items => - items.filter(item => item.category === category) + items.filter(item => item.category === category), ) interface Props { @@ -204,7 +204,7 @@ interface Props { const MyComponent: FC = ({ category }) => { const selectItemsByCategory = useMemo( () => makeSelectItemsByCategory(category), - [category] + [category], ) const itemsByCategory = useSelector(selectItemsByCategory) @@ -229,13 +229,13 @@ import { createSelector } from 'reselect' const makeSelectItemsByCategory = category => createSelector([state => state.items], items => - items.filter(item => item.category === category) + items.filter(item => item.category === category), ) const MyComponent = ({ category }) => { const selectItemsByCategory = useMemo( () => makeSelectItemsByCategory(category), - [category] + [category], ) const itemsByCategory = useSelector(selectItemsByCategory) @@ -263,9 +263,9 @@ import { createCachedSelector } from 're-reselect' const selectItemsByCategory = createCachedSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], - (items, category) => items.filter(item => item.category === category) + (items, category) => items.filter(item => item.category === category), )((state: RootState, category: string) => category) ``` @@ -291,20 +291,20 @@ const state: RootState = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], (items, category) => items.filter(item => item.category === category), { memoize: weakMapMemoize, - argsMemoize: weakMapMemoize - } + argsMemoize: weakMapMemoize, + }, ) selectItemsByCategory(state, 'Electronics') // Selector runs @@ -324,8 +324,8 @@ const state = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( @@ -333,8 +333,8 @@ const selectItemsByCategory = createSelector( (items, category) => items.filter(item => item.category === category), { memoize: weakMapMemoize, - argsMemoize: weakMapMemoize - } + argsMemoize: weakMapMemoize, + }, ) selectItemsByCategory(state, 'Electronics') // Selector runs @@ -390,20 +390,20 @@ const state: RootState = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], (items, category) => items.filter(item => item.category === category), { memoize: weakMapMemoize, - argsMemoize: weakMapMemoize - } + argsMemoize: weakMapMemoize, + }, ) selectItemsByCategory(state, 'Electronics') // Selector runs @@ -423,8 +423,8 @@ const state = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const selectItemsByCategory = createSelector( @@ -432,8 +432,8 @@ const selectItemsByCategory = createSelector( (items, category) => items.filter(item => item.category === category), { memoize: weakMapMemoize, - argsMemoize: weakMapMemoize - } + argsMemoize: weakMapMemoize, + }, ) selectItemsByCategory(state, 'Electronics') // Selector runs @@ -469,21 +469,21 @@ const state: RootState = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const createSelectorWeakMap = createSelectorCreator({ memoize: weakMapMemoize, - argsMemoize: weakMapMemoize + argsMemoize: weakMapMemoize, }) const selectItemsByCategory = createSelectorWeakMap( [ (state: RootState) => state.items, - (state: RootState, category: string) => category + (state: RootState, category: string) => category, ], - (items, category) => items.filter(item => item.category === category) + (items, category) => items.filter(item => item.category === category), ) selectItemsByCategory(state, 'Electronics') // Selector runs @@ -503,18 +503,18 @@ const state = { { id: 1, category: 'Electronics', name: 'Wireless Headphones' }, { id: 2, category: 'Books', name: 'The Great Gatsby' }, { id: 3, category: 'Home Appliances', name: 'Blender' }, - { id: 4, category: 'Stationery', name: 'Sticky Notes' } - ] + { id: 4, category: 'Stationery', name: 'Sticky Notes' }, + ], } const createSelectorWeakMap = createSelectorCreator({ memoize: weakMapMemoize, - argsMemoize: weakMapMemoize + argsMemoize: weakMapMemoize, }) const selectItemsByCategory = createSelectorWeakMap( [state => state.items, (state, category) => category], - (items, category) => items.filter(item => item.category === category) + (items, category) => items.filter(item => item.category === category), ) selectItemsByCategory(state, 'Electronics') // Selector runs diff --git a/website/docs/introduction/getting-started.mdx b/website/docs/introduction/getting-started.mdx index 6f8fe1471..9615e9571 100644 --- a/website/docs/introduction/getting-started.mdx +++ b/website/docs/introduction/getting-started.mdx @@ -70,12 +70,12 @@ interface RootState { const state: RootState = { todos: [ { id: 0, completed: false }, - { id: 1, completed: true } + { id: 1, completed: true }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } const selectCompletedTodos = (state: RootState) => { @@ -92,7 +92,7 @@ const memoizedSelectCompletedTodos = createSelector( todos => { console.log('memoized selector ran') return todos.filter(todo => todo.completed === true) - } + }, ) memoizedSelectCompletedTodos(state) // memoized selector ran @@ -102,7 +102,7 @@ memoizedSelectCompletedTodos(state) console.log(selectCompletedTodos(state) === selectCompletedTodos(state)) //=> false console.log( - memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state) + memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state), ) //=> true ``` @@ -115,12 +115,12 @@ import { createSelector } from 'reselect' const state = { todos: [ { id: 0, completed: false }, - { id: 1, completed: true } + { id: 1, completed: true }, ], alerts: [ { id: 0, read: false }, - { id: 1, read: true } - ] + { id: 1, read: true }, + ], } const selectCompletedTodos = state => { @@ -137,7 +137,7 @@ const memoizedSelectCompletedTodos = createSelector( todos => { console.log('memoized selector ran') return todos.filter(todo => todo.completed === true) - } + }, ) memoizedSelectCompletedTodos(state) // memoized selector ran @@ -147,7 +147,7 @@ memoizedSelectCompletedTodos(state) console.log(selectCompletedTodos(state) === selectCompletedTodos(state)) //=> false console.log( - memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state) + memoizedSelectCompletedTodos(state) === memoizedSelectCompletedTodos(state), ) //=> true ``` @@ -165,14 +165,15 @@ In addition to skipping unnecessary recalculations, `memoizedSelectCompletedTodo ## Terminology - Selector Function} /> - : A function that accepts one or more JavaScript values as arguments, and derives - a result. When used with , the first argument is typically - the entire Redux store state. + : A function that accepts one or more JavaScript values as arguments, and + derives a result. When used with , the first argument + is typically the entire Redux store state. - Input Selectors} /> - : Basic selector functions used as building blocks for creating a memoized selector. - They are passed as the first argument(s) to , and - are called with all selector arguments. They are responsible for extracting and - providing necessary values to the . + : Basic selector functions used as building blocks for creating a memoized + selector. They are passed as the first argument(s) to + , and are called with all selector arguments. They are responsible for + extracting and providing necessary values to the + . - Output Selector} /> : The actual memoized selectors created by . - Result Function} /> @@ -188,6 +189,6 @@ The below example serves as a visual aid: ```ts const outputSelector = createSelector( [inputSelector1, inputSelector2, inputSelector3], // synonymous with `dependencies`. - resultFunc // Result function + resultFunc, // Result function ) ``` diff --git a/website/docs/introduction/how-does-reselect-work.mdx b/website/docs/introduction/how-does-reselect-work.mdx index 04f171c94..16dcea8bb 100644 --- a/website/docs/introduction/how-does-reselect-work.mdx +++ b/website/docs/introduction/how-does-reselect-work.mdx @@ -16,7 +16,7 @@ Reselect, at its core, is a library for creating memoized selectors in JavaScrip ```ts const finalSelector = (...args) => { const extractedValues = inputSelectors.map(inputSelector => - inputSelector(...args) + inputSelector(...args), ) return resultFunc(...extractedValues) } @@ -91,7 +91,7 @@ So you decide to memoize it: ```ts const selectCompletedTodos = someMemoizeFunction((state: RootState) => - state.todos.filter(todo => todo.completed === true) + state.todos.filter(todo => todo.completed === true), ) ``` @@ -119,7 +119,7 @@ But with Reselect, we can do something like this: ```ts const selectCompletedTodos = createSelector( [(state: RootState) => state.todos], - todos => todos.filter(todo => todo.completed === true) + todos => todos.filter(todo => todo.completed === true), ) ``` diff --git a/website/docs/usage/best-practices.mdx b/website/docs/usage/best-practices.mdx index 9ad500dc8..b2c16334d 100644 --- a/website/docs/usage/best-practices.mdx +++ b/website/docs/usage/best-practices.mdx @@ -24,7 +24,7 @@ This: // ✔️ This is optimal because we have less calculations in input selectors and more in the result function. const selectorGood = createSelector( [(state: RootState) => state.todos], - todos => someExpensiveComputation(todos) + todos => someExpensiveComputation(todos), ) ``` @@ -34,6 +34,6 @@ Is preferable to this: // ❌ This is not optimal! const selectorBad = createSelector( [(state: RootState) => someExpensiveComputation(state.todos)], - someOtherCalculation + someOtherCalculation, ) ``` diff --git a/website/docs/usage/common-mistakes.mdx b/website/docs/usage/common-mistakes.mdx index 497dd0cab..57f072ad2 100644 --- a/website/docs/usage/common-mistakes.mdx +++ b/website/docs/usage/common-mistakes.mdx @@ -16,7 +16,7 @@ A somewhat common mistake is to write an { readdirSync(directory, { - withFileTypes: true + withFileTypes: true, }).forEach(entry => { const filePath = path.join(directory, entry.name) if (entry.isDirectory()) { @@ -42,7 +42,7 @@ const insertCodeExamples = (examplesDirectory: string) => { : `import TabItem from '@theme/TabItem'\n` content = content.replace( frontMatterRegex, - frontMatter => `${frontMatter}\n${importTabs}${importTabItem}` + frontMatter => `${frontMatter}\n${importTabs}${importTabItem}`, ) content = content.replace( @@ -55,14 +55,14 @@ const insertCodeExamples = (examplesDirectory: string) => { const jsFileName = tsFileName.replace( tsExtensionRegex, - `.${jsFileExtension}` + `.${jsFileExtension}`, ) const tsFilePath = path.join(examplesDirectory, tsFileName) const jsFilePath = path.join( examplesDirectory, getTSConfig(examplesDirectory).compilerOptions.outDir, - tsFileName.replace(tsExtensionRegex, `.${jsFileExtension}`) + tsFileName.replace(tsExtensionRegex, `.${jsFileExtension}`), ) const tsFileContent = readFileSync(tsFilePath, 'utf-8') @@ -92,7 +92,7 @@ const insertCodeExamples = (examplesDirectory: string) => { {/* END: ${tsFileName} */}` - } + }, ) writeFileSync(markdownFilePath, content) }) diff --git a/website/monokaiTheme.js b/website/monokaiTheme.js index 8b5ffb46b..bd3eebd14 100644 --- a/website/monokaiTheme.js +++ b/website/monokaiTheme.js @@ -1,62 +1,62 @@ module.exports = { plain: { color: '#f8f8f2', - backgroundColor: '#272822' + backgroundColor: '#272822', }, styles: [ { types: ['comment', 'prolog', 'doctype', 'cdata'], style: { - color: '#778090' - } + color: '#778090', + }, }, { types: ['punctuation'], style: { - color: '#F8F8F2' - } + color: '#F8F8F2', + }, }, { types: ['property', 'tag', 'constant', 'symbol', 'deleted'], style: { - color: '#F92672' - } + color: '#F92672', + }, }, { types: ['boolean', 'number'], style: { - color: '#AE81FF' - } + color: '#AE81FF', + }, }, { types: ['selector', 'attr-name', 'string', 'char', 'builtin', 'inserted'], style: { - color: '#a6e22e' - } + color: '#a6e22e', + }, }, { types: ['operator', 'entity', 'url', 'variable'], style: { - color: '#F8F8F2' - } + color: '#F8F8F2', + }, }, { types: ['atrule', 'attr-value', 'function'], style: { - color: '#E6D874' - } + color: '#E6D874', + }, }, { types: ['keyword'], style: { - color: '#F92672' - } + color: '#F92672', + }, }, { types: ['regex', 'important'], style: { - color: '#FD971F' - } - } - ] + color: '#FD971F', + }, + }, + ], } diff --git a/website/sidebars.ts b/website/sidebars.ts index eceea148f..f6594c546 100644 --- a/website/sidebars.ts +++ b/website/sidebars.ts @@ -21,8 +21,8 @@ const sidebars: SidebarsConfig = { items: [ 'introduction/getting-started', 'introduction/how-does-reselect-work', - 'introduction/v5-summary' - ] + 'introduction/v5-summary', + ], }, { type: 'category', @@ -40,10 +40,10 @@ const sidebars: SidebarsConfig = { items: [ 'api/lruMemoize', 'api/weakMapMemoize', - 'api/unstable_autotrackMemoize' - ] - } - ] + 'api/unstable_autotrackMemoize', + ], + }, + ], }, { @@ -52,13 +52,13 @@ const sidebars: SidebarsConfig = { items: [ 'usage/best-practices', 'usage/common-mistakes', - 'usage/handling-empty-array-results' - ] + 'usage/handling-empty-array-results', + ], }, 'FAQ', 'external-references', - 'related-projects' - ] + 'related-projects', + ], } export default sidebars diff --git a/website/src/components/ExternalLinks.tsx b/website/src/components/ExternalLinks.tsx index 6348cfef8..cf24f93b4 100644 --- a/website/src/components/ExternalLinks.tsx +++ b/website/src/components/ExternalLinks.tsx @@ -68,7 +68,7 @@ export const ExternalLinks = { {text} - )) + )), } as const satisfies Record> export const AllExternalLinks: FC = memo(() => { diff --git a/website/src/components/HomepageFeatures/index.tsx b/website/src/components/HomepageFeatures/index.tsx index 3e2930951..4b6038c81 100644 --- a/website/src/components/HomepageFeatures/index.tsx +++ b/website/src/components/HomepageFeatures/index.tsx @@ -1,82 +1,82 @@ -import Heading from '@theme/Heading' -import clsx from 'clsx' -import type { FC, JSX } from 'react' -import { memo } from 'react' -import styles from './styles.module.css' - -interface FeatureItem { - title: string - description: JSX.Element -} - -const FeatureList: FeatureItem[] = [ - { - title: 'Predictable', - description: ( - <> - Like Redux, Reselect gives users a consistent mental model for - memoizing functions. Extract input values, recalculate when any input - changes. - - ) - }, - { - title: 'Optimized', - description: ( - <> - Reselect{' '} - - minimizes the number of times expensive computations are performed - - , reuses existing result references if nothing has changed, and improves - performance. - - ) - }, - { - title: 'Customizable', - description: ( - <> - Reselect comes with fast defaults, but provides{' '} - flexible customization options. Swap memoization methods, change - equality checks, and customize for your needs. - - ) - }, - { - title: 'Type-Safe', - description: ( - <> - Reselect is designed for great TypeScript support. Generated - selectors infer all types from input selectors. - - ) - } -] - -const Feature: FC = memo(({ title, description }) => { - return ( -

-
- {title} -

{description}

-
-
- ) -}) - -const HomepageFeatures: FC = () => { - return ( -
-
-
- {FeatureList.map((props, idx) => ( - - ))} -
-
-
- ) -} - -export default memo(HomepageFeatures) +import Heading from '@theme/Heading' +import clsx from 'clsx' +import type { FC, JSX } from 'react' +import { memo } from 'react' +import styles from './styles.module.css' + +interface FeatureItem { + title: string + description: JSX.Element +} + +const FeatureList: FeatureItem[] = [ + { + title: 'Predictable', + description: ( + <> + Like Redux, Reselect gives users a consistent mental model for + memoizing functions. Extract input values, recalculate when any input + changes. + + ), + }, + { + title: 'Optimized', + description: ( + <> + Reselect{' '} + + minimizes the number of times expensive computations are performed + + , reuses existing result references if nothing has changed, and improves + performance. + + ), + }, + { + title: 'Customizable', + description: ( + <> + Reselect comes with fast defaults, but provides{' '} + flexible customization options. Swap memoization methods, change + equality checks, and customize for your needs. + + ), + }, + { + title: 'Type-Safe', + description: ( + <> + Reselect is designed for great TypeScript support. Generated + selectors infer all types from input selectors. + + ), + }, +] + +const Feature: FC = memo(({ title, description }) => { + return ( +
+
+ {title} +

{description}

+
+
+ ) +}) + +const HomepageFeatures: FC = () => { + return ( +
+
+
+ {FeatureList.map((props, idx) => ( + + ))} +
+
+
+ ) +} + +export default memo(HomepageFeatures) diff --git a/website/src/components/InternalLinks.tsx b/website/src/components/InternalLinks.tsx index 4c30b2427..a70ebbd5a 100644 --- a/website/src/components/InternalLinks.tsx +++ b/website/src/components/InternalLinks.tsx @@ -89,5 +89,5 @@ export const InternalLinks = { createStructuredSelector - )) + )), } as const satisfies Record> diff --git a/website/src/components/PackageManagerTabs.tsx b/website/src/components/PackageManagerTabs.tsx index 4fb2c51d5..82ecca473 100644 --- a/website/src/components/PackageManagerTabs.tsx +++ b/website/src/components/PackageManagerTabs.tsx @@ -10,7 +10,7 @@ const packageManagers = [ { value: 'npm', label: 'NPM', command: 'install' }, { value: 'yarn', label: 'Yarn', command: 'add' }, { value: 'bun', label: 'Bun', command: 'add' }, - { value: 'pnpm', label: 'PNPM', command: 'add' } + { value: 'pnpm', label: 'PNPM', command: 'add' }, ] as const const PackageManagerTabs: FC = () => { diff --git a/website/src/css/custom.css b/website/src/css/custom.css index c545afd6c..63cc45251 100644 --- a/website/src/css/custom.css +++ b/website/src/css/custom.css @@ -165,11 +165,15 @@ a:visited { transform: rotateZ(180deg); -webkit-transition: -webkit-transform 0.2s linear; transition: -webkit-transform 0.2s linear; - transition-property: transform, -webkit-transform; + transition-property: + transform, + -webkit-transform; transition-duration: 0.2s, 0.2s; transition-timing-function: linear, linear; transition-delay: 0s, 0s; - transition: transform 0.2s linear, -webkit-transform 0.2s linear; + transition: + transform 0.2s linear, + -webkit-transform 0.2s linear; color: var(--ifm-font-base-color); } @@ -252,4 +256,4 @@ table.checkbox-table tbody td { content: ' TYPESCRIPT'; color: var(--ifm-color-info); position: relative; -} \ No newline at end of file +}