reselect-kit 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +307 -36
  2. package/dist/cjs/reselectKit.development.cjs +256 -95
  3. package/dist/cjs/reselectKit.development.cjs.map +1 -1
  4. package/dist/cjs/reselectKit.production.min.cjs +1 -1
  5. package/dist/cjs/reselectKit.production.min.cjs.map +1 -1
  6. package/dist/reselectKit.d.ts +201 -92
  7. package/dist/reselectKit.mjs +257 -97
  8. package/dist/reselectKit.mjs.map +1 -1
  9. package/package.json +3 -3
  10. package/src/__tests__/createAdaptedSelector.test.ts +3 -4
  11. package/src/__tests__/createBoundSelector.test.ts +92 -5
  12. package/src/__tests__/createChainSelector.bench.ts +117 -0
  13. package/src/__tests__/createChainSelector.test.ts +162 -6
  14. package/src/__tests__/createPathSelector.test.ts +14 -0
  15. package/src/__tests__/createSequenceSelector.test.ts +10 -14
  16. package/src/_helpers/__tests__/generateSelectorKey.test.ts +24 -0
  17. package/src/_helpers/__tests__/getSelectorName.test.ts +30 -0
  18. package/src/_helpers/unfreezeProps.ts +14 -0
  19. package/src/_hooks/__tests__/useSelector.bench.ts +133 -0
  20. package/src/_hooks/__tests__/useSelector.test.ts +69 -0
  21. package/src/_hooks/__tests__/useSelectors.bench.ts +160 -0
  22. package/src/_hooks/__tests__/useSelectors.test.ts +219 -0
  23. package/src/_hooks/selectorOptions.ts +11 -0
  24. package/src/_hooks/useSelector.ts +68 -26
  25. package/src/_hooks/useSelectors.ts +162 -0
  26. package/src/cache/__tests__/intervalMapCache.test.ts +11 -0
  27. package/src/createAdaptedSelector.ts +9 -5
  28. package/src/createBoundSelector.ts +92 -84
  29. package/src/createCachedSequenceSelector.ts +12 -8
  30. package/src/createCachedStructuredSelector.ts +11 -20
  31. package/src/createChainSelector.ts +134 -89
  32. package/src/createPathSelector.ts +71 -104
  33. package/src/createPropSelector.ts +9 -8
  34. package/src/createSequenceSelector.ts +14 -9
  35. package/src/index.ts +2 -0
  36. package/src/keys/createKeySelectorComposer.ts +12 -23
  37. package/src/types.ts +115 -7
@@ -1,5 +1,4 @@
1
- import { Selector } from '@veksa/reselect';
2
- import { NamedParametricSelector, NamedSelector, Path } from './types';
1
+ import { NamedSelector, Path } from './types';
3
2
  import { isObject } from './_helpers/isObject';
4
3
  import { withDebugName } from './debug/withDebugName';
5
4
  import { getSelectorName } from './_helpers/getSelectorName';
@@ -10,131 +9,94 @@ export type IsOptional<T> = undefined extends T ? true : null extends T ? true :
10
9
 
11
10
  export type IsObject<T> = T extends object ? true : false;
12
11
 
13
- export type PathSelector<S, R, D> = NamedSelector<S, R, [], D> & {
14
- path: Path;
15
- };
16
-
17
- export type PathParametricSelector<S, P, R, D> = NamedParametricSelector<S, P, R, D> & {
12
+ /**
13
+ * The selector a path builder produces. `Params` is carried through untouched
14
+ * from the base selector: walking a path changes the result, never the
15
+ * arguments, so the plain and the parametric case are the same type.
16
+ */
17
+ export type PathSelector<S, R, Params extends readonly any[], D> = NamedSelector<
18
+ S,
19
+ R,
20
+ Params,
21
+ D
22
+ > & {
18
23
  path: Path;
19
24
  };
20
25
 
21
26
  type Required<T> = { [P in keyof T]-?: Exclude<T[P], undefined | null> };
22
27
 
23
- export type RequiredSelectorBuilder<S, R, D> = R extends object
24
- ? { [K in keyof R]: () => PathSelector<S, NonNullable<R[K]>, D> }
25
- : {};
26
-
27
- export type OptionalSelectorBuilder<S, R, D> = ((
28
- defaultValue?: R,
29
- ) => PathSelector<S, R extends undefined ? Defined<R> : R, D>) &
30
- (R extends object
31
- ? { [K in keyof Required<R>]: () => PathSelector<S, NonNullable<R[K]>, D> }
32
- : {});
33
-
34
- export type RequiredParametricSelectorBuilder<S, P, R, D> = R extends object
35
- ? { [K in keyof R]: () => PathParametricSelector<S, P, NonNullable<R[K]>, D> }
28
+ export type RequiredSelectorBuilder<S, R, Params extends readonly any[], D> = R extends object
29
+ ? { [K in keyof R]: () => PathSelector<S, NonNullable<R[K]>, Params, D> }
36
30
  : {};
37
31
 
38
- export type OptionalParametricSelectorBuilder<S, P, R, D> = ((
32
+ export type OptionalSelectorBuilder<S, R, Params extends readonly any[], D> = ((
39
33
  defaultValue?: R,
40
- ) => PathParametricSelector<S, P, R extends undefined ? Defined<R> : R, D>) &
34
+ ) => PathSelector<S, R extends undefined ? Defined<R> : R, Params, D>) &
41
35
  (R extends object
42
- ? { [K in keyof Required<R>]: () => PathParametricSelector<S, P, NonNullable<R[K]>, D> }
36
+ ? { [K in keyof Required<R>]: () => PathSelector<S, NonNullable<R[K]>, Params, D> }
43
37
  : {});
44
38
 
45
- export type RequiredObjectSelectorWrapper<S, R, D> = {
39
+ export type RequiredObjectSelectorWrapper<S, R, Params extends readonly any[], D> = {
46
40
  [K in keyof R]-?: IsOptional<R[K]> extends true
47
- ? OptionalPathSelectorType<S, R[K], D>
48
- : RequiredPathSelectorType<S, R[K], D>;
49
- };
50
-
51
- export type OptionalObjectSelectorWrapper<S, R, D> = {
52
- [K in keyof R]-?: OptionalPathSelectorType<S, R[K], D>;
53
- };
54
-
55
- export type RequiredObjectParametricSelectorWrapper<S, P, R, D> = {
56
- [K in keyof R]-?: IsOptional<R[K]> extends true
57
- ? OptionalPathParametricSelectorType<S, P, R[K], D>
58
- : RequiredPathParametricSelectorType<S, P, R[K], D>;
59
- };
60
-
61
- export type OptionalObjectParametricSelectorWrapper<S, P, R, D> = {
62
- [K in keyof R]-?: OptionalPathParametricSelectorType<S, P, R[K], D>;
41
+ ? OptionalPathSelectorType<S, R[K], Params, D>
42
+ : RequiredPathSelectorType<S, R[K], Params, D>;
63
43
  };
64
44
 
65
- export type RequiredArraySelectorWrapper<S, R, D> = {
66
- length: RequiredPathSelectorType<S, number, D>;
67
-
68
- [K: number]: IsOptional<R> extends true
69
- ? OptionalPathSelectorType<S, R, D>
70
- : RequiredPathSelectorType<S, R, D>;
45
+ export type OptionalObjectSelectorWrapper<S, R, Params extends readonly any[], D> = {
46
+ [K in keyof R]-?: OptionalPathSelectorType<S, R[K], Params, D>;
71
47
  };
72
48
 
73
- export type OptionalArraySelectorWrapper<S, R, D> = {
74
- length: RequiredPathSelectorType<S, number, D>;
75
-
76
- [K: number]: OptionalPathSelectorType<S, R, D>;
77
- };
78
-
79
- export type RequiredArrayParametricSelectorWrapper<S, P, R, D> = {
80
- length: RequiredPathParametricSelectorType<S, P, number, D>;
49
+ export type RequiredArraySelectorWrapper<S, R, Params extends readonly any[], D> = {
50
+ length: RequiredPathSelectorType<S, number, Params, D>;
81
51
 
82
52
  [K: number]: IsOptional<R> extends true
83
- ? OptionalPathParametricSelectorType<S, P, R, D>
84
- : RequiredPathParametricSelectorType<S, P, R, D>;
53
+ ? OptionalPathSelectorType<S, R, Params, D>
54
+ : RequiredPathSelectorType<S, R, Params, D>;
85
55
  };
86
56
 
87
- export type OptionalArrayParametricSelectorWrapper<S, P, R, D> = {
88
- length: RequiredPathParametricSelectorType<S, P, number, D>;
57
+ export type OptionalArraySelectorWrapper<S, R, Params extends readonly any[], D> = {
58
+ length: RequiredPathSelectorType<S, number, Params, D>;
89
59
 
90
- [K: number]: OptionalPathParametricSelectorType<S, P, R, D>;
60
+ [K: number]: OptionalPathSelectorType<S, R, Params, D>;
91
61
  };
92
62
 
93
- export type RequiredDataSelectorWrapper<S, R, D> = R extends unknown[]
94
- ? RequiredArraySelectorWrapper<S, R[number], D>
95
- : IsObject<R> extends true
96
- ? RequiredObjectSelectorWrapper<S, R, D>
97
- : RequiredSelectorBuilder<S, R, D>;
98
-
99
- export type OptionalDataSelectorWrapper<S, R, D> = R extends unknown[]
100
- ? OptionalArraySelectorWrapper<S, R[number], D>
101
- : IsObject<R> extends true
102
- ? OptionalObjectSelectorWrapper<S, R, D>
103
- : OptionalSelectorBuilder<S, R, D>;
104
-
105
- export type RequiredDataParametricSelectorWrapper<S, P, R, D> = R extends unknown[]
106
- ? RequiredArrayParametricSelectorWrapper<S, P, R[number], D>
63
+ export type RequiredDataSelectorWrapper<
64
+ S,
65
+ R,
66
+ Params extends readonly any[],
67
+ D,
68
+ > = R extends unknown[]
69
+ ? RequiredArraySelectorWrapper<S, R[number], Params, D>
107
70
  : IsObject<R> extends true
108
- ? RequiredObjectParametricSelectorWrapper<S, P, R, D>
109
- : RequiredParametricSelectorBuilder<S, P, R, D>;
71
+ ? RequiredObjectSelectorWrapper<S, R, Params, D>
72
+ : RequiredSelectorBuilder<S, R, Params, D>;
110
73
 
111
- export type OptionalDataParametricSelectorWrapper<S, P, R, D> = R extends unknown[]
112
- ? OptionalArrayParametricSelectorWrapper<S, P, R[number], D>
74
+ export type OptionalDataSelectorWrapper<
75
+ S,
76
+ R,
77
+ Params extends readonly any[],
78
+ D,
79
+ > = R extends unknown[]
80
+ ? OptionalArraySelectorWrapper<S, R[number], Params, D>
113
81
  : IsObject<R> extends true
114
- ? OptionalObjectParametricSelectorWrapper<S, P, R, D>
115
- : OptionalParametricSelectorBuilder<S, P, R, D>;
82
+ ? OptionalObjectSelectorWrapper<S, R, Params, D>
83
+ : OptionalSelectorBuilder<S, R, Params, D>;
116
84
 
117
- export type RequiredPathSelectorType<S, R, D> = RequiredSelectorBuilder<S, R, D> &
118
- RequiredDataSelectorWrapper<S, NonNullable<R>, D>;
119
-
120
- export type OptionalPathSelectorType<S, R, D> = OptionalSelectorBuilder<S, R, D> &
121
- OptionalDataSelectorWrapper<S, NonNullable<R>, D>;
122
-
123
- export type RequiredPathParametricSelectorType<S, P, R, D> = RequiredParametricSelectorBuilder<
85
+ export type RequiredPathSelectorType<
124
86
  S,
125
- P,
126
87
  R,
127
- D
128
- > &
129
- RequiredDataParametricSelectorWrapper<S, P, NonNullable<R>, D>;
88
+ Params extends readonly any[],
89
+ D,
90
+ > = RequiredSelectorBuilder<S, R, Params, D> &
91
+ RequiredDataSelectorWrapper<S, NonNullable<R>, Params, D>;
130
92
 
131
- export type OptionalPathParametricSelectorType<S, P, R, D> = OptionalParametricSelectorBuilder<
93
+ export type OptionalPathSelectorType<
132
94
  S,
133
- P,
134
95
  R,
135
- D
136
- > &
137
- OptionalDataParametricSelectorWrapper<S, P, NonNullable<R>, D>;
96
+ Params extends readonly any[],
97
+ D,
98
+ > = OptionalSelectorBuilder<S, R, Params, D> &
99
+ OptionalDataSelectorWrapper<S, NonNullable<R>, Params, D>;
138
100
 
139
101
  const defaultVoidFunction = () => {
140
102
  return;
@@ -195,14 +157,19 @@ export const innerCreatePathSelector = (
195
157
  });
196
158
  };
197
159
 
198
- export function createPathSelector<S, R, P = void>(
199
- baseSelector: P extends void ? Selector<S, R> : Selector<S, R, [P]>,
200
- ): P extends void
201
- ? IsOptional<R> extends true
202
- ? OptionalPathSelectorType<S, R, [Selector<S, R>]>
203
- : RequiredPathSelectorType<S, R, [Selector<S, R>]>
204
- : IsOptional<R> extends true
205
- ? OptionalPathParametricSelectorType<S, P, R, [Selector<S, R, [P]>]>
206
- : RequiredPathParametricSelectorType<S, P, R, [Selector<S, R, [P]>]> {
160
+ /**
161
+ * Builds a proxy that turns property access into a selector reading that path.
162
+ *
163
+ * `Params` is inferred straight from the base selector, so the plain and the
164
+ * parametric case share one signature — and one set of builder types — instead
165
+ * of being told apart by a conditional in the parameter position. It defaults
166
+ * to empty so that `createPathSelector<State, Result>(selector)` keeps working
167
+ * for callers that spell the type arguments out.
168
+ */
169
+ export function createPathSelector<S, R, Params extends readonly any[] = []>(
170
+ baseSelector: (state: S, ...params: Params) => R,
171
+ ): IsOptional<R> extends true
172
+ ? OptionalPathSelectorType<S, R, Params, [typeof baseSelector]>
173
+ : RequiredPathSelectorType<S, R, Params, [typeof baseSelector]> {
207
174
  return innerCreatePathSelector(baseSelector as (...args: unknown[]) => unknown) as any;
208
175
  }
@@ -1,5 +1,5 @@
1
1
  import { Selector } from '@veksa/reselect';
2
- import { innerCreatePathSelector, RequiredPathParametricSelectorType } from './createPathSelector';
2
+ import { innerCreatePathSelector, RequiredPathSelectorType } from './createPathSelector';
3
3
  import { Path } from './types';
4
4
 
5
5
  const propSelectorSymbol = Symbol.for('PropSelector');
@@ -8,10 +8,10 @@ export const isPropSelector = (selector: unknown): selector is { path: Path } =>
8
8
  return selector instanceof Object && propSelectorSymbol in selector;
9
9
  };
10
10
 
11
- export function createPropSelector<P>(): RequiredPathParametricSelectorType<
11
+ export function createPropSelector<P>(): RequiredPathSelectorType<
12
12
  unknown,
13
13
  P,
14
- P,
14
+ [P],
15
15
  [Selector<unknown, P, [P]>]
16
16
  > {
17
17
  const propsSelector = (_state: unknown, props: unknown) => props;
@@ -20,9 +20,10 @@ export function createPropSelector<P>(): RequiredPathParametricSelectorType<
20
20
  Object.defineProperty(selector, propSelectorSymbol, { value: true });
21
21
  };
22
22
 
23
- return innerCreatePathSelector(
24
- propsSelector,
25
- [],
26
- applyMeta,
27
- ) as RequiredPathParametricSelectorType<unknown, P, P, [Selector<unknown, P, [P]>]>;
23
+ return innerCreatePathSelector(propsSelector, [], applyMeta) as RequiredPathSelectorType<
24
+ unknown,
25
+ P,
26
+ [P],
27
+ [Selector<unknown, P, [P]>]
28
+ >;
28
29
  }
@@ -1,14 +1,19 @@
1
- import { createSelector, Selector } from '@veksa/reselect';
1
+ import { createSelector, Selector, SelectorResultArray } from '@veksa/reselect';
2
+ import { AnySelector, MergedParams, MergedState } from './types';
2
3
 
3
- export function createSequenceSelector<S, R>(
4
- selectors: Selector<S, R>[],
4
+ /**
5
+ * Runs a group of selectors and collects their results positionally.
6
+ *
7
+ * The selectors are inferred as a tuple rather than an array, which is what
8
+ * makes the result a tuple of the individual result types and lets `state` and
9
+ * the extra arguments be merged per position. An array would collapse them into
10
+ * a union and resolve everything past `state` to `never`, which is why the
11
+ * plain and the parametric case used to need an overload each.
12
+ */
13
+ export function createSequenceSelector<Selectors extends readonly AnySelector[]>(
14
+ selectors: [...Selectors],
5
15
  selectorCreator?: typeof createSelector,
6
- ): Selector<S, R[]>;
7
-
8
- export function createSequenceSelector<S, P, R>(
9
- selectors: Selector<S, R, [P]>[],
10
- selectorCreator?: typeof createSelector,
11
- ): Selector<S, R[], [P]>;
16
+ ): Selector<MergedState<Selectors>, SelectorResultArray<Selectors>, MergedParams<Selectors>>;
12
17
 
13
18
  export function createSequenceSelector(selectors: any, selectorCreator = createSelector) {
14
19
  return selectorCreator(selectors, (...results: unknown[]) => results);
package/src/index.ts CHANGED
@@ -7,6 +7,7 @@ export { createBoundSelector } from './createBoundSelector';
7
7
  export { createEmptySelector } from './createEmptySelector';
8
8
  export { createAdaptedSelector } from './createAdaptedSelector';
9
9
  export { createChainSelector, type ChainSelectorOptions } from './createChainSelector';
10
+ export { createSequenceSelector } from './createSequenceSelector';
10
11
  export { createCachedSequenceSelector } from './createCachedSequenceSelector';
11
12
  export { createCachedStructuredSelector } from './createCachedStructuredSelector';
12
13
 
@@ -24,3 +25,4 @@ export { initGarbageCollector, IntervalMapCache } from './cache/intervalMapCache
24
25
  export { TreeCache } from './cache/TreeCache';
25
26
 
26
27
  export { useSelector } from './_hooks/useSelector';
28
+ export { useSelectors } from './_hooks/useSelectors';
@@ -1,5 +1,5 @@
1
1
  import { KeySelector } from '@veksa/re-reselect';
2
- import { UnionToIntersection } from '../types';
2
+ import { MergedParams, MergedState } from '../types';
3
3
 
4
4
  export const composedKeySelectorSymbol = Symbol.for('ComposedKeySelector');
5
5
 
@@ -9,39 +9,28 @@ export const composedKeySelectorSymbol = Symbol.for('ComposedKeySelector');
9
9
  */
10
10
  type AnyKeySelector = KeySelector<any>;
11
11
 
12
- /** Intersection of every input key selector's `state` argument. */
13
- type ComposedState<Ks extends readonly AnyKeySelector[]> = UnionToIntersection<
14
- { [Index in keyof Ks]: Parameters<Ks[Index]>[0] }[number]
15
- >;
16
-
17
- /** Union of every input key selector's `props` argument (position 1), if any. */
18
- type PropsUnion<Ks extends readonly AnyKeySelector[]> = {
19
- [Index in keyof Ks]: Parameters<Ks[Index]> extends [any, infer Props, ...any[]] ? Props : never;
20
- }[number];
21
-
22
- /** Intersection of every input key selector's `props`, or `never` if none are parametric. */
23
- type ComposedProps<Ks extends readonly AnyKeySelector[]> = [PropsUnion<Ks>] extends [never]
24
- ? never
25
- : UnionToIntersection<PropsUnion<Ks>>;
26
-
27
12
  /**
28
13
  * The key selector produced by a {@link KeySelectorComposer}: a function that
29
- * intersects the `state` (and `props`, when any input is parametric) of every
30
- * composed key selector, tagged with the original inputs as `dependencies`.
14
+ * intersects the `state` and the extra arguments of every composed key
15
+ * selector, tagged with the original inputs as `dependencies`.
31
16
  */
32
- export type OutputKeySelector<S, P, D extends readonly AnyKeySelector[]> = ([P] extends [never]
33
- ? (state: S) => unknown
34
- : (state: S, props: P) => unknown) & {
17
+ export type OutputKeySelector<
18
+ S,
19
+ Params extends readonly any[],
20
+ D extends readonly AnyKeySelector[],
21
+ > = ((state: S, ...params: Params) => unknown) & {
35
22
  dependencies: D;
36
23
  };
37
24
 
38
25
  /**
39
26
  * Composes N key selectors into a single one. A single variadic signature with
40
- * tuple inference replaces the former per-arity overload set.
27
+ * tuple inference replaces the former per-arity overload set; `state` and the
28
+ * extra arguments are merged by the same helpers every other multi-selector
29
+ * creator in the library uses.
41
30
  */
42
31
  export type KeySelectorComposer = <Ks extends readonly AnyKeySelector[]>(
43
32
  ...keySelectors: Ks
44
- ) => OutputKeySelector<ComposedState<Ks>, ComposedProps<Ks>, Ks>;
33
+ ) => OutputKeySelector<MergedState<Ks>, MergedParams<Ks>, Ks>;
45
34
 
46
35
  export function isComposedKeySelector(
47
36
  keySelector: AnyKeySelector,
package/src/types.ts CHANGED
@@ -1,6 +1,17 @@
1
- import { Selector, SelectorArray } from '@veksa/reselect';
1
+ import {
2
+ GetParamsFromSelectors,
3
+ GetStateFromSelectors,
4
+ Selector,
5
+ SelectorArray,
6
+ } from '@veksa/reselect';
2
7
  import { OutputCachedSelector } from '@veksa/re-reselect';
3
8
 
9
+ /**
10
+ * The loosest shape a selector can take: a function of `state` plus any number
11
+ * of extra arguments. Used as the constraint for variadic tuple inference.
12
+ */
13
+ export type AnySelector = (state: any, ...params: any[]) => any;
14
+
4
15
  /**
5
16
  * A selector carrying optional debug metadata. In the unified model a
6
17
  * "parametric" selector is simply a selector whose `Params` tuple is non-empty,
@@ -18,12 +29,6 @@ export type NamedSelector<S, R, Params extends readonly any[] = [], D = unknown[
18
29
  dependencies?: D;
19
30
  };
20
31
 
21
- /**
22
- * Back-compat alias: a parametric selector is a {@link NamedSelector} whose
23
- * single extra argument is `Props`.
24
- */
25
- export type NamedParametricSelector<S, P, R, D = unknown[]> = NamedSelector<S, R, [P], D>;
26
-
27
32
  export type ReReselectSelector = OutputCachedSelector<SelectorArray, unknown>;
28
33
 
29
34
  export type CachedSelector = Pick<ReReselectSelector, 'cache' | 'keySelector'> &
@@ -41,3 +46,106 @@ export type UnionToIntersection<Union> = (
41
46
  ) extends (merged: infer Intersection) => void
42
47
  ? Intersection
43
48
  : never;
49
+
50
+ /** The values of a group of selectors, whether it is an object or a tuple. */
51
+ type SelectorsUnion<Selectors> = Selectors extends readonly AnySelector[]
52
+ ? Selectors[number]
53
+ : Selectors[keyof Selectors];
54
+
55
+ /**
56
+ * The `state` argument of each selector in a union of them. Distributed over
57
+ * the union rather than matched against it, so each member is read on its own.
58
+ */
59
+ type StateOf<S> = S extends AnySelector ? Parameters<S>[0] : never;
60
+
61
+ /**
62
+ * The `props` argument of the selectors in a union that require one, or `never`
63
+ * for the ones that take none. The argument tuple is matched rather than the
64
+ * signature: a selector of `state` alone is assignable to one that also takes
65
+ * `props`, and would otherwise contribute `unknown`.
66
+ */
67
+ type RequiredPropsOf<S> = S extends AnySelector
68
+ ? Parameters<S> extends [any, infer Props, ...any[]]
69
+ ? Props
70
+ : never
71
+ : never;
72
+
73
+ /**
74
+ * The `props` argument of the selectors in a union that accept one without
75
+ * requiring it. An optional argument does not match the tuple pattern above,
76
+ * which is why it needs a pattern of its own; `undefined` is what the optional
77
+ * marker contributes and is dropped, so that intersecting the props of the
78
+ * group does not collapse to `never`.
79
+ *
80
+ * A selector that leaves its arguments open — `Selector<State, Result>` is
81
+ * `(state, ...args: any[]) => Result` — declares no props at all and must
82
+ * contribute nothing, or its `any` would swallow the props of every other
83
+ * selector in the group. It is told apart by the length of what follows
84
+ * `state`: an optional argument gives a finite `0 | 1`, a rest gives `number`.
85
+ */
86
+ type OptionalPropsOf<S> = S extends AnySelector
87
+ ? Parameters<S> extends [any, any, ...any[]]
88
+ ? never
89
+ : Parameters<S> extends [any, ...infer Rest]
90
+ ? number extends Rest['length']
91
+ ? never
92
+ : Rest extends readonly []
93
+ ? never
94
+ : Exclude<Rest[0], undefined>
95
+ : never
96
+ : never;
97
+
98
+ /**
99
+ * The `state` every selector in the group accepts: the intersection of their
100
+ * first arguments. An empty group constrains nothing, so it reads `unknown`
101
+ * rather than the `never` an intersection of no members would otherwise give.
102
+ */
103
+ export type MergedState<Selectors> = Selectors extends readonly []
104
+ ? unknown
105
+ : Selectors extends readonly AnySelector[]
106
+ ? GetStateFromSelectors<Selectors>
107
+ : UnionToIntersection<StateOf<SelectorsUnion<Selectors>>>;
108
+
109
+ /**
110
+ * The props of a union of selectors, as the argument tuple that reads all of
111
+ * them: empty when none takes props, optional when every selector that takes
112
+ * them declares them optional, required as soon as one selector requires them.
113
+ * The props themselves are intersected, so the argument satisfies each member.
114
+ */
115
+ type UnionProps<Union, Required = RequiredPropsOf<Union>, Optional = OptionalPropsOf<Union>> = [
116
+ Required,
117
+ ] extends [never]
118
+ ? [Optional] extends [never]
119
+ ? []
120
+ : [props?: UnionToIntersection<Optional>]
121
+ : [props: UnionToIntersection<Required | Optional>];
122
+
123
+ /**
124
+ * The extra arguments the group takes beyond `state`: `[]` when no selector is
125
+ * parametric, `[Props]` when some are — with the `Props` of every parametric
126
+ * selector intersected.
127
+ *
128
+ * A tuple of selectors is merged position by position, so a selector taking
129
+ * more than `props` keeps its later arguments. An object is merged through the
130
+ * union of its values instead, which stops at `props` but costs nothing per
131
+ * key: the keys carry no order worth recovering, and a batch of selectors has
132
+ * no reason to be capped at a size a component could plausibly reach.
133
+ */
134
+ export type MergedParams<Selectors> = Selectors extends readonly AnySelector[]
135
+ ? GetParamsFromSelectors<Selectors>
136
+ : UnionProps<SelectorsUnion<Selectors>>;
137
+
138
+ /**
139
+ * The same element-wise merge as {@link MergedParams}, for two argument tuples
140
+ * that are already known — the case of a step-by-step composition, where each
141
+ * step adds its own arguments to what the previous steps require.
142
+ */
143
+ export type MergeParams<A extends readonly any[], B extends readonly any[]> = A extends readonly []
144
+ ? B
145
+ : B extends readonly []
146
+ ? A
147
+ : A extends readonly [infer AHead, ...infer ATail]
148
+ ? B extends readonly [infer BHead, ...infer BTail]
149
+ ? [AHead & BHead, ...MergeParams<ATail, BTail>]
150
+ : A
151
+ : B;