Skip to content

Commit

Permalink
Fix issues with createStructuredSelector parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Nov 17, 2021
1 parent 75b6e01 commit 7ed20fc
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 11 deletions.
16 changes: 12 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import type {
DropFirst,
MergeParameters,
Expand,
ObjValueTuple
ObjValueTuple,
Head,
Tail
} from './types'

export type {
Expand Down Expand Up @@ -213,13 +215,19 @@ export const createSelector =
type SelectorsObject = { [key: string]: (...args: any[]) => any }

export interface StructuredSelectorCreator {
<SelectorMap extends SelectorsObject>(
<
SelectorMap extends SelectorsObject,
SelectorParams = MergeParameters<ObjValueTuple<SelectorMap>>
>(
selectorMap: SelectorMap,
selectorCreator?: CreateSelectorFunction<any, any, any>
): (
// Accept an arbitrary number of parameters for all selectors
// @ts-ignore
...params: [...MergeParameters<ObjValueTuple<SelectorMap>>]
// The annoying head/tail bit here is because TS isn't convinced that
// the `SelectorParams` type is really an array, so we launder things.
// Plus it matches common usage anyway.
state: Head<SelectorParams>,
...params: Tail<SelectorParams>
) => {
[Key in keyof SelectorMap]: ReturnType<SelectorMap[Key]>
}
Expand Down
2 changes: 1 addition & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ export type ExpandItems<T extends readonly unknown[]> = {
}

/** First item in an array */
export type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never
export type Head<T> = T extends [any, ...any[]] ? T[0] : never
/** All other items in an array */
export type Tail<A> = A extends [any, ...infer Rest] ? Rest : never

Expand Down
29 changes: 23 additions & 6 deletions typescript_test/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,16 @@ function testCreateSelectorCreator() {
}

function testCreateStructuredSelector() {
const oneParamSelector = createStructuredSelector({
foo: (state: StateAB) => state.a,
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
})

const selector = createStructuredSelector<
{ foo: string },
{
Expand All @@ -619,9 +629,9 @@ function testCreateStructuredSelector() {
bar: state => +state.foo
})

const res = selector({ foo: '42' })
const foo: string = res.foo
const bar: number = res.bar
const res1 = selector({ foo: '42' })
const foo: string = res1.foo
const bar: number = res1.bar

// @ts-expect-error
selector({ bar: '42' })
Expand All @@ -646,8 +656,8 @@ function testCreateStructuredSelector() {

// Test automatic inference of types for createStructuredSelector via overload
type State = { foo: string }
const FooSelector = (state: State) => state.foo
const BarSelector = (state: State) => +state.foo
const FooSelector = (state: State, a: number, b: string) => state.foo
const BarSelector = (state: State, a: number, b: string) => +state.foo

const selector2 = createStructuredSelector({
foo: FooSelector,
Expand All @@ -667,8 +677,15 @@ function testCreateStructuredSelector() {
bar: number
}

const resOneParam = oneParamSelector({ a: 1, b: 2 })
const resThreeParams = threeParamSelector({ a: 1, b: 2 }, 99, 'blah')
const res2: ExpectedResult = selector({ foo: '42' })
const resGenerics: ExpectedResult = selectorGenerics({ foo: '42' })
const res3: ExpectedResult = selector2({ foo: '42' }, 99, 'test')
const resGenerics: ExpectedResult = selectorGenerics(
{ foo: '42' },
99,
'test'
)

//@ts-expect-error
selector2({ bar: '42' })
Expand Down

0 comments on commit 7ed20fc

Please sign in to comment.