UNPKG

6.44 kBPlain TextView Raw
1import type {
2 UnknownFunction,
3 Expand,
4 TuplifyUnion,
5 Has,
6 List,
7 IsTuple
8} from '../types'
9
10/** Given a set of input selectors, extracts the intersected parameters to determine
11 * what values can actually be passed to all of the input selectors at once
12 * WARNING: "you are not expected to understand this" :)
13 */
14export type MergeParameters<
15 // The actual array of input selectors
16 T extends readonly UnknownFunction[],
17 // Given those selectors, we do several transformations on the types in sequence:
18 // 1) Extract "the type of parameters" for each input selector, so that we now have
19 // a tuple of all those parameters
20 ParamsArrays extends readonly any[][] = ExtractParams<T>,
21 // 2) Transpose the parameter tuples.
22 // Originally, we have nested arrays with "all params from input", "from input 2", etc:
23 // `[ [i1a, i1b, i1c], [i2a, i2b, i2c], [i3a, i3b, i3c] ],
24 // In order to intersect the params at each index, we need to transpose them so that
25 // we have "all the first args", "all the second args", and so on:
26 // `[ [i1a, i2a, i3a], [i1b, i2b, i3b], [i1c, i2c, i3c] ]
27 // Unfortunately, this step also turns the arrays into a union, and weirder, it is
28 // a union of all possible combinations for all input functions, so there's duplicates.
29 TransposedArrays = Transpose<ParamsArrays>,
30 // 3) Turn the union of arrays back into a nested tuple. Order does not matter here.
31 TuplifiedArrays extends any[] = TuplifyUnion<TransposedArrays>,
32 // 4) Find the longest params array out of the ones we have.
33 // Note that this is actually the _nested_ data we wanted out of the transpose step,
34 // so it has all the right pieces we need.
35 LongestParamsArray extends readonly any[] = LongestArray<TuplifiedArrays>
36> =
37 // After all that preparation work, we can actually do parameter extraction.
38 // These steps work somewhat inside out (jump ahead to the middle):
39 // 11) Finally, after all that, run a shallow expansion on the values to make the user-visible
40 // field details more readable when viewing the selector's type in a hover box.
41 ExpandItems<
42 // 10) Tuples can have field names attached, and it seems to work better to remove those
43 RemoveNames<{
44 // 5) We know the longest params array has N args. Loop over the indices of that array.
45 // 6) For each index, do a check to ensure that we're _only_ checking numeric indices,
46 // not any field names for array functions like `slice()`
47 [index in keyof LongestParamsArray]: LongestParamsArray[index] extends LongestParamsArray[number]
48 ? // 9) Any object types that were intersected may have had
49 IgnoreInvalidIntersections<
50 // 8) Then, intersect all of the parameters for this arg together.
51 IntersectAll<
52 // 7) Since this is a _nested_ array, extract the right sub-array for this index
53 LongestParamsArray[index]
54 >
55 >
56 : never
57 }>
58 >
59
60/*
61 *
62 * Reselect Internal Utility Types
63 *
64 */
65
66/*
67 *
68 * Reselect Internal Utility Types
69 *
70 */
71
72/** An object with no fields */
73type EmptyObject = {
74 [K in any]: never
75}
76
77type IgnoreInvalidIntersections<T> = T extends EmptyObject ? never : T
78
79/** Extract the parameters from all functions as a tuple */
80export type ExtractParams<T extends readonly UnknownFunction[]> = {
81 [index in keyof T]: T[index] extends T[number] ? Parameters<T[index]> : never
82}
83
84/** Recursively expand all fields in an object for easier reading */
85export type ExpandItems<T extends readonly unknown[]> = {
86 [index in keyof T]: T[index] extends T[number] ? Expand<T[index]> : never
87}
88
89/** Select the longer of two arrays */
90export type Longest<L extends List, L1 extends List> = L extends unknown
91 ? L1 extends unknown
92 ? { 0: L1; 1: L }[Has<keyof L, keyof L1>]
93 : never
94 : never
95
96/** Recurse over a nested array to locate the longest one.
97 * Acts like a type-level `reduce()`
98 */
99export type LongestArray<S extends readonly any[][]> =
100 // If this isn't a tuple, all indices are the same, we can't tell a difference
101 IsTuple<S> extends '0'
102 ? // so just return the type of the first item
103 S[0]
104 : // If there's two nested arrays remaining, compare them
105 S extends [any[], any[]]
106 ? Longest<S[0], S[1]>
107 : // If there's more than two, extract their types, treat the remainder as a smaller array
108 S extends [any[], any[], ...infer Rest]
109 ? // then compare those two, recurse through the smaller array, and compare vs its result
110 Longest<
111 Longest<S[0], S[1]>,
112 Rest extends any[][] ? LongestArray<Rest> : []
113 >
114 : // If there's one item left, return it
115 S extends [any[]]
116 ? S[0]
117 : never
118
119/** Recursive type for intersecting together all items in a tuple, to determine
120 * the final parameter type at a given argument index in the generated selector. */
121export type IntersectAll<T extends any[]> = IsTuple<T> extends '0'
122 ? T[0]
123 : _IntersectAll<T>
124
125type IfJustNullish<T, True, False> = [T] extends [undefined | null]
126 ? True
127 : False
128
129/** Intersect a pair of types together, for use in parameter type calculation.
130 * This is made much more complex because we need to correctly handle cases
131 * where a function has fewer parameters and the type is `undefined`, as well as
132 * optional params or params that have `null` or `undefined` as part of a union.
133 *
134 * If the next type by itself is `null` or `undefined`, we exclude it and return
135 * the other type. Otherwise, intersect them together.
136 */
137type _IntersectAll<T, R = unknown> = T extends [infer First, ...infer Rest]
138 ? _IntersectAll<Rest, IfJustNullish<First, R, R & First>>
139 : R
140
141/*
142 *
143 * External/Copied Utility Types
144 *
145 */
146
147/**
148 * Removes field names from a tuple
149 * Source: https://stackoverflow.com/a/63571175/62937
150 */
151type RemoveNames<T extends readonly any[]> = [any, ...T] extends [
152 any,
153 ...infer U
154]
155 ? U
156 : never
157
158/**
159 * Transposes nested arrays
160 * Source: https://stackoverflow.com/a/66303933/62937
161 */
162type Transpose<T> = T[Extract<
163 keyof T,
164 T extends readonly any[] ? number : unknown
165>] extends infer V
166 ? {
167 [K in keyof V]: {
168 [L in keyof T]: K extends keyof T[L] ? T[L][K] : undefined
169 }
170 }
171 : never