From 71edb87512b021fb6636486fed68d5113e806744 Mon Sep 17 00:00:00 2001 From: Julian Coy Date: Tue, 26 Oct 2021 15:20:29 -0400 Subject: [PATCH 1/4] 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/4] 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/4] 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/4] 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 {