diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f26313ce0..776d7d979 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Node uses: actions/setup-node@v2 with: - node-version: 14.x + node-version: 16.x cache: 'yarn' - name: Install dependencies @@ -39,8 +39,8 @@ jobs: strategy: fail-fast: false matrix: - node: ['14.x'] - ts: ['4.0', '4.1', '4.2', '4.3', '4.4', '4.5', '4.6', 'next'] + node: ['16.x'] + ts: ['4.1', '4.2', '4.3', '4.4', '4.5', '4.6', '4.7', '4.8', '4.9.2-rc'] steps: - name: Checkout repo uses: actions/checkout@v2 diff --git a/src/hooks/useSelector.ts b/src/hooks/useSelector.ts index 034b2d216..626b0b483 100644 --- a/src/hooks/useSelector.ts +++ b/src/hooks/useSelector.ts @@ -2,7 +2,7 @@ import { useContext, useDebugValue } from 'react' import { useReduxContext as useDefaultReduxContext } from './useReduxContext' import { ReactReduxContext } from '../components/Context' -import type { EqualityFn } from '../types' +import type { EqualityFn, NoInfer } from '../types' import type { uSESWS } from '../utils/useSyncExternalStore' import { notInitialized } from '../utils/useSyncExternalStore' @@ -32,7 +32,7 @@ export function createSelectorHook( return function useSelector( selector: (state: TState) => Selected, - equalityFn: EqualityFn = refEquality + equalityFn: EqualityFn> = refEquality ): Selected { if (process.env.NODE_ENV !== 'production') { if (!selector) { diff --git a/src/types.ts b/src/types.ts index 5a8017d1e..90ecebe8d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -165,6 +165,8 @@ export type ResolveThunks = TDispatchProps extends { export interface TypedUseSelectorHook { ( selector: (state: TState) => TSelected, - equalityFn?: EqualityFn + equalityFn?: EqualityFn> ): TSelected } + +export type NoInfer = [T][T extends any ? 0 : never] diff --git a/test/typetests/hooks.tsx b/test/typetests/hooks.tsx index 84475e961..9bbce00e4 100644 --- a/test/typetests/hooks.tsx +++ b/test/typetests/hooks.tsx @@ -34,7 +34,7 @@ import { fetchCount, } from './counterApp' -import { expectType } from '../typeTestHelpers' +import { expectType, expectExactType } from '../typeTestHelpers' function preTypedHooksSetup() { // Standard hooks setup @@ -87,6 +87,20 @@ function testShallowEqual() { shallowEqual({ test: 'test' }, { test: 'test' }) shallowEqual({ test: 'test' }, 'a') const x: boolean = shallowEqual('a', 'a') + + type TestState = { stateProp: string } + + // Additionally, it should infer its type from arguments and not become `any` + const selected1 = useSelector( + (state: TestState) => state.stateProp, + shallowEqual + ) + expectExactType(selected1) + + const useAppSelector: TypedUseSelectorHook = useSelector + + const selected2 = useAppSelector((state) => state.stateProp, shallowEqual) + expectExactType(selected2) } function testUseDispatch() {