From 71edb87512b021fb6636486fed68d5113e806744 Mon Sep 17 00:00:00 2001 From: Julian Coy Date: Tue, 26 Oct 2021 15:20:29 -0400 Subject: [PATCH 1/5] Split out optional last argument (memoizeOptions) into it's own overload since TS can't figure out spread unions easily --- src/index.ts | 30 +++++++++++++++++++++--------- typescript_test/test.ts | 24 ++++++++++++++++++++++-- 2 files changed, 43 insertions(+), 11 deletions(-) diff --git a/src/index.ts b/src/index.ts index 6f24bf25f..4f04c0684 100644 --- a/src/index.ts +++ b/src/index.ts @@ -52,7 +52,7 @@ type DropFirst = T extends [unknown, ...infer U] : never export function createSelectorCreator< - F extends(...args: unknown[]) => unknown, + F extends (...args: unknown[]) => unknown, MemoizeFunction extends (func: F, ...options: any[]) => F, MemoizeOptions extends unknown[] = DropFirst> >( @@ -98,7 +98,7 @@ export function createSelectorCreator< // we wrap it in an array so we can apply it. const finalMemoizeOptions = Array.isArray(memoizeOptions) ? memoizeOptions - : ([ memoizeOptions ] as MemoizeOptions) + : ([memoizeOptions] as MemoizeOptions) const dependencies = getDependencies(funcs) @@ -160,13 +160,25 @@ interface CreateSelectorFunction< > { /** Input selectors as separate inline arguments */ ( - ...items: - | [...Selectors, (...args: SelectorResultArray) => Result] - | [ - ...Selectors, - (...args: SelectorResultArray) => Result, - CreateSelectorOptions - ] + ...items: [ + ...Selectors, + (...args: SelectorResultArray) => Result + ] + ): OutputSelector< + Selectors, + Result, + GetParamsFromSelectors, + ((...args: SelectorResultArray) => Result) & + ReturnType + > + + /** Input selectors as separate inline arguments with memoizeOptions passed */ + ( + ...items: [ + ...Selectors, + (...args: SelectorResultArray) => Result, + CreateSelectorOptions + ] ): OutputSelector< Selectors, Result, diff --git a/typescript_test/test.ts b/typescript_test/test.ts index 7a722ec69..edc53afc8 100644 --- a/typescript_test/test.ts +++ b/typescript_test/test.ts @@ -5,7 +5,8 @@ import { createSelectorCreator, createStructuredSelector, ParametricSelector, - OutputSelector + OutputSelector, + SelectorResultArray } from '../src/index' import microMemoize from 'micro-memoize' @@ -162,8 +163,8 @@ function testInvalidTypeInCombinator() { // does not allow heterogeneous parameter type // selectors when the combinator function is typed differently + // @ts-expect-error createSelector( - // @ts-expect-error (state: { testString: string }) => state.testString, (state: { testNumber: number }) => state.testNumber, (state: { testBoolean: boolean }) => state.testBoolean, @@ -990,3 +991,22 @@ function createSelectorConfigOptions() { } ) } + +function testInputSelectorWithUndefinedReturn() { + type Input = { field: number | undefined } + type Output = string + type SelectorType = (input: Input) => Output + + // Make sure the selector type is honored + const selector: SelectorType = createSelector( + (input: Input) => input.field, + args => 'test' + ) + + // even when memoizeOptions are passed + const selector2: SelectorType = createSelector( + (input: Input) => input.field, + input => 'test', + { memoizeOptions: { maxSize: 42 } } + ) +} From 95a02c32bd9c1e7e2987e822f2b4af48b3e09c14 Mon Sep 17 00:00:00 2001 From: Julian Coy Date: Tue, 26 Oct 2021 16:29:22 -0400 Subject: [PATCH 2/5] Add test for function inferrences --- typescript_test/test.ts | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/typescript_test/test.ts b/typescript_test/test.ts index edc53afc8..f2108da41 100644 --- a/typescript_test/test.ts +++ b/typescript_test/test.ts @@ -997,16 +997,25 @@ function testInputSelectorWithUndefinedReturn() { type Output = string type SelectorType = (input: Input) => Output + const input = ({ field }: Input) => field + const result = (out: number | undefined): Output => 'test' + // Make sure the selector type is honored const selector: SelectorType = createSelector( - (input: Input) => input.field, + ({ field }: Input) => field, args => 'test' ) // even when memoizeOptions are passed const selector2: SelectorType = createSelector( - (input: Input) => input.field, - input => 'test', + ({ field }: Input) => field, + args => 'test', { memoizeOptions: { maxSize: 42 } } ) + + // Make sure inference of functions works... + const selector3: SelectorType = createSelector(input, result) + const selector4: SelectorType = createSelector(input, result, { + memoizeOptions: { maxSize: 42 } + }) } From a2d0722791ab3ad8bdcc33d867e07a1eb6e6bc46 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Tue, 26 Oct 2021 18:48:52 -0400 Subject: [PATCH 3/5] Silence more annoying lint rules --- .eslintrc | 5 +++-- typescript_test/test.ts | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index 77be52a54..d1c7292a4 100644 --- a/.eslintrc +++ b/.eslintrc @@ -54,7 +54,7 @@ } }, { - "files": ["**/test/**/*.ts"], + "files": ["**/test/**/*.ts", "**/typescript_test/**/*.ts"], "rules": { "consistent-return": "off", "max-lines": "off", @@ -67,7 +67,8 @@ "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/camelcase": "off", "import/max-dependencies": "off", - "sonarjs/no-duplicate-string": "off" + "sonarjs/no-duplicate-string": "off", + "@typescript-eslint/no-shadow": "off" } } ] diff --git a/typescript_test/test.ts b/typescript_test/test.ts index f2108da41..c1e1fd959 100644 --- a/typescript_test/test.ts +++ b/typescript_test/test.ts @@ -992,6 +992,7 @@ function createSelectorConfigOptions() { ) } +// Issue #526 function testInputSelectorWithUndefinedReturn() { type Input = { field: number | undefined } type Output = string From 9a3b415b5f6b6fee1d84b283a1a2f66be5f07ac3 Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Tue, 26 Oct 2021 18:49:03 -0400 Subject: [PATCH 4/5] Export missed OutputParametricSelector type --- src/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index 4f04c0684..3b9630707 100644 --- a/src/index.ts +++ b/src/index.ts @@ -14,7 +14,8 @@ export type { EqualityFn, SelectorArray, SelectorResultArray, - ParametricSelector + ParametricSelector, + OutputParametricSelector } from './types' import { From b800e2fa3ba2efd1b2d45c7c335daae8f92b912c Mon Sep 17 00:00:00 2001 From: Mark Erikson Date: Tue, 26 Oct 2021 19:00:45 -0400 Subject: [PATCH 5/5] Use an actual type for the selector count checks --- typescript_test/test.ts | 116 ++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/typescript_test/test.ts b/typescript_test/test.ts index da314eb31..fde067db9 100644 --- a/typescript_test/test.ts +++ b/typescript_test/test.ts @@ -994,68 +994,70 @@ function createSelectorConfigOptions() { ) } +// Verify more than 12 selectors are accepted +// Issue #525 const withLotsOfInputSelectors = createSelector( - (_state: any) => 1, - (_state: any) => 2, - (_state: any) => 3, - (_state: any) => 4, - (_state: any) => 5, - (_state: any) => 6, - (_state: any) => 7, - (_state: any) => 8, - (_state: any) => 9, - (_state: any) => 10, - (_state: any) => 11, - (_state: any) => 12, - (_state: any) => 13, - (_state: any) => 14, - (_state: any) => 15, - (_state: any) => 16, - (_state: any) => 17, - (_state: any) => 18, - (_state: any) => 19, - (_state: any) => 20, - (_state: any) => 21, - (_state: any) => 22, - (_state: any) => 23, - (_state: any) => 24, - (_state: any) => 25, - (_state: any) => 26, - (_state: any) => 27, - (_state: any) => 28, + (_state: StateA) => 1, + (_state: StateA) => 2, + (_state: StateA) => 3, + (_state: StateA) => 4, + (_state: StateA) => 5, + (_state: StateA) => 6, + (_state: StateA) => 7, + (_state: StateA) => 8, + (_state: StateA) => 9, + (_state: StateA) => 10, + (_state: StateA) => 11, + (_state: StateA) => 12, + (_state: StateA) => 13, + (_state: StateA) => 14, + (_state: StateA) => 15, + (_state: StateA) => 16, + (_state: StateA) => 17, + (_state: StateA) => 18, + (_state: StateA) => 19, + (_state: StateA) => 20, + (_state: StateA) => 21, + (_state: StateA) => 22, + (_state: StateA) => 23, + (_state: StateA) => 24, + (_state: StateA) => 25, + (_state: StateA) => 26, + (_state: StateA) => 27, + (_state: StateA) => 28, (...args) => args.length ) type SelectorArray29 = [ - (_state: any) => 1, - (_state: any) => 2, - (_state: any) => 3, - (_state: any) => 4, - (_state: any) => 5, - (_state: any) => 6, - (_state: any) => 7, - (_state: any) => 8, - (_state: any) => 9, - (_state: any) => 10, - (_state: any) => 11, - (_state: any) => 12, - (_state: any) => 13, - (_state: any) => 14, - (_state: any) => 15, - (_state: any) => 16, - (_state: any) => 17, - (_state: any) => 18, - (_state: any) => 19, - (_state: any) => 20, - (_state: any) => 21, - (_state: any) => 22, - (_state: any) => 23, - (_state: any) => 24, - (_state: any) => 25, - (_state: any) => 26, - (_state: any) => 27, - (_state: any) => 28, - (_state: any) => 29 + (_state: StateA) => 1, + (_state: StateA) => 2, + (_state: StateA) => 3, + (_state: StateA) => 4, + (_state: StateA) => 5, + (_state: StateA) => 6, + (_state: StateA) => 7, + (_state: StateA) => 8, + (_state: StateA) => 9, + (_state: StateA) => 10, + (_state: StateA) => 11, + (_state: StateA) => 12, + (_state: StateA) => 13, + (_state: StateA) => 14, + (_state: StateA) => 15, + (_state: StateA) => 16, + (_state: StateA) => 17, + (_state: StateA) => 18, + (_state: StateA) => 19, + (_state: StateA) => 20, + (_state: StateA) => 21, + (_state: StateA) => 22, + (_state: StateA) => 23, + (_state: StateA) => 24, + (_state: StateA) => 25, + (_state: StateA) => 26, + (_state: StateA) => 27, + (_state: StateA) => 28, + (_state: StateA) => 29 ] type Results = SelectorResultArray