inferred-types 0.27.0 → 0.28.2
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.
- package/.vscode/settings.json +4 -0
- package/dist/index.d.ts +363 -165
- package/dist/index.js +64 -24
- package/dist/index.mjs +60 -23
- package/package.json +1 -1
- package/src/types/Mutable.ts +3 -1
- package/src/types/TypeInfo/Extends.ts +14 -0
- package/src/types/TypeInfo/IsBooleanLiteral.ts +9 -0
- package/src/types/TypeInfo/IsLiteral.ts +36 -2
- package/src/types/TypeInfo/IsNumericLiteral.ts +9 -0
- package/src/types/TypeInfo/IsObject.ts +27 -0
- package/src/types/TypeInfo/IsScalar.ts +14 -0
- package/src/types/TypeInfo/IsStringLiteral.ts +9 -0
- package/src/types/TypeInfo/IsUndefined.ts +14 -0
- package/src/types/TypeInfo/TypeDefault.ts +58 -0
- package/src/types/TypeInfo/index.ts +4 -0
- package/src/types/dictionary/MapTo.ts +247 -36
- package/src/types/dictionary/props.ts +11 -0
- package/src/types/kv/DictFromKv.ts +1 -1
- package/src/types/type-testing.ts +2 -5
- package/src/utility/dictionary/kv/kvToDict.ts +1 -2
- package/src/utility/dictionary/mapTo.ts +122 -77
- package/src/utility/dictionary/merge.ts +36 -0
- package/src/utility/runtime/conditions/isObject.ts +2 -16
- package/tests/TypeInfo/IsLiteral.spec.ts +17 -1
- package/tests/dictionary/IntersectingKeys.test.ts +42 -0
- package/tests/dictionary/TypeDefault.test.ts +76 -0
- package/tests/dictionary/mapTo.test.ts +214 -48
- package/tests/dictionary/merge.test.ts +41 -0
- package/tests/withValue.spec.ts +0 -1
|
@@ -1,10 +1,17 @@
|
|
|
1
|
+
/* eslint-disable no-use-before-define */
|
|
1
2
|
import { OptRequired } from "src/types/literal-unions";
|
|
3
|
+
import {
|
|
4
|
+
DefaultManyToOneMapping,
|
|
5
|
+
DefaultOneToManyMapping,
|
|
6
|
+
DefaultOneToOneMapping,
|
|
7
|
+
} from "src/utility/dictionary/mapTo";
|
|
2
8
|
import { EnumValues } from "../EnumValues";
|
|
9
|
+
import { TypeDefault } from "../TypeInfo/TypeDefault";
|
|
3
10
|
|
|
4
11
|
/**
|
|
5
12
|
* Expresses relationship between inputs/outputs:
|
|
6
13
|
*/
|
|
7
|
-
export enum
|
|
14
|
+
export enum MapCardinality {
|
|
8
15
|
/** every input results in 0:M outputs */
|
|
9
16
|
OneToMany = "I -> O[]",
|
|
10
17
|
/** every input results in 0:1 outputs */
|
|
@@ -13,35 +20,230 @@ export enum MapDirection {
|
|
|
13
20
|
ManyToOne = "I[] -> O",
|
|
14
21
|
}
|
|
15
22
|
|
|
16
|
-
export type
|
|
23
|
+
export type MapCardinalityIllustrated = EnumValues<MapCardinality>;
|
|
17
24
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
/**
|
|
26
|
+
* The _user_ configuration of a **mapTo** mapper function
|
|
27
|
+
* which will be finalized by merging it with the appropriate
|
|
28
|
+
* default mapping type.
|
|
29
|
+
*/
|
|
30
|
+
export interface MapConfig<
|
|
31
|
+
IR extends OptRequired | undefined = undefined,
|
|
32
|
+
D extends MapCardinalityIllustrated | undefined = undefined,
|
|
33
|
+
OR extends OptRequired | undefined = undefined
|
|
34
|
+
> {
|
|
35
|
+
input?: IR;
|
|
36
|
+
output?: OR;
|
|
37
|
+
cardinality?: D;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A finalized configuration of a **mapTo** mapper functions cardinality
|
|
42
|
+
* relationships between _inputs_ and _outputs_.
|
|
43
|
+
*
|
|
44
|
+
* Note: _this configuration does _not_ yet include the actual mapping
|
|
45
|
+
* configuration between the input and output._
|
|
46
|
+
*/
|
|
47
|
+
export type FinalizedMapConfig<
|
|
48
|
+
IR extends OptRequired,
|
|
49
|
+
D extends MapCardinalityIllustrated,
|
|
50
|
+
OR extends OptRequired
|
|
51
|
+
> = Required<MapConfig<IR, D, OR>> & { finalized: true };
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* User configuration exposed by a config function which specifies the
|
|
55
|
+
* cardinality already (e.g., `oneToMany()`, `manyToOne()`)
|
|
56
|
+
*/
|
|
57
|
+
type MapCardinalityConfig<
|
|
58
|
+
IR extends OptRequired | undefined,
|
|
59
|
+
OR extends OptRequired | undefined
|
|
60
|
+
> = {
|
|
61
|
+
input?: IR;
|
|
62
|
+
output?: OR;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* **ConfiguredMap**
|
|
67
|
+
*
|
|
68
|
+
* A partial application of the mapTo() utility which has expressed the
|
|
69
|
+
* configuration of the _inputs_ and _outputs_ and provides a `.map()`
|
|
70
|
+
* method which allows the user configure the specifics of the mapping.
|
|
71
|
+
*/
|
|
72
|
+
export type ConfiguredMap<
|
|
73
|
+
C extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>
|
|
74
|
+
> = {
|
|
75
|
+
map: <I, O>(map: MapTo<I, O, C>) => MapFn<I, O, C>;
|
|
76
|
+
input: MapIR<C>;
|
|
77
|
+
cardinality: MapCard<C>;
|
|
78
|
+
output: MapOR<C>;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Extracts the IR, Cardinality, and OR generics from a FinalizedMapConfig
|
|
83
|
+
*/
|
|
84
|
+
export type DecomposeMapConfig<
|
|
85
|
+
M extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>
|
|
86
|
+
> = M extends MapConfig<infer IR, infer D, infer OR>
|
|
87
|
+
? IR extends OptRequired | undefined
|
|
88
|
+
? D extends MapCardinalityIllustrated | undefined
|
|
89
|
+
? OR extends OptRequired | undefined
|
|
90
|
+
? [IR, D, OR]
|
|
91
|
+
: never
|
|
92
|
+
: never
|
|
93
|
+
: never
|
|
94
|
+
: M extends FinalizedMapConfig<infer IR, infer D, infer OR>
|
|
95
|
+
? IR extends OptRequired
|
|
96
|
+
? D extends MapCardinalityIllustrated
|
|
97
|
+
? OR extends OptRequired
|
|
98
|
+
? [IR, D, OR]
|
|
99
|
+
: never
|
|
100
|
+
: never
|
|
101
|
+
: never
|
|
102
|
+
: never;
|
|
103
|
+
|
|
104
|
+
/** extracts IR from a `FinalizedMapConfig` */
|
|
105
|
+
export type MapIR<
|
|
106
|
+
T extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>
|
|
107
|
+
> = DecomposeMapConfig<T>[0];
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* extracts the MapCardinality from a `FinalizedMapConfig`
|
|
111
|
+
*/
|
|
112
|
+
export type MapCard<
|
|
113
|
+
T extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>
|
|
114
|
+
> = DecomposeMapConfig<T>[1];
|
|
115
|
+
|
|
116
|
+
/** extracts OR from a `FinalizedMapConfig` */
|
|
117
|
+
export type MapOR<
|
|
118
|
+
T extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>
|
|
119
|
+
> = DecomposeMapConfig<T>[2];
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Merges the types of a userland configuration with a default configuration
|
|
123
|
+
*/
|
|
124
|
+
export type AsFinalizedConfig<
|
|
125
|
+
U extends MapConfig<
|
|
126
|
+
OptRequired | undefined,
|
|
127
|
+
MapCardinalityIllustrated | undefined,
|
|
128
|
+
OptRequired | undefined
|
|
129
|
+
>,
|
|
130
|
+
D extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>
|
|
131
|
+
> = TypeDefault<U, D> extends FinalizedMapConfig<infer IR, infer C, infer OR>
|
|
132
|
+
? FinalizedMapConfig<IR, C, OR>
|
|
133
|
+
: never;
|
|
134
|
+
|
|
135
|
+
export type MapperApi = {
|
|
136
|
+
/**
|
|
137
|
+
* Provides opportunity to configure _input_, _output_, and _cardinality_
|
|
138
|
+
* prior to providing a mapping function.
|
|
139
|
+
*
|
|
140
|
+
* Note: _the defaults for configuration are defined by the_ `DEFAULT_ONE_TO_MANY_MAPPING`
|
|
141
|
+
* _constant made available as a symbol from this library._
|
|
142
|
+
*/
|
|
143
|
+
config: <
|
|
144
|
+
C extends MapConfig<
|
|
145
|
+
OptRequired, //
|
|
146
|
+
MapCardinalityIllustrated,
|
|
147
|
+
OptRequired
|
|
148
|
+
>
|
|
149
|
+
>(
|
|
150
|
+
config: C
|
|
151
|
+
) => ConfiguredMap<
|
|
152
|
+
AsFinalizedConfig<
|
|
153
|
+
C, //
|
|
154
|
+
DefaultOneToManyMapping
|
|
155
|
+
>
|
|
156
|
+
>;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Provides a nice 1:1 ratio between the input and output.
|
|
160
|
+
*
|
|
161
|
+
* By default the input and output are considered to be _required_
|
|
162
|
+
* properties but this can be changed with the options hash provided.
|
|
163
|
+
* ```ts
|
|
164
|
+
* const mapper = mapTo.oneToOne().map(...);
|
|
165
|
+
* // add in ability to filter out some inputs
|
|
166
|
+
* const mapAndFilter = mapTo.oneToOne({ output: "opt" })
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
oneToOne: <C extends MapCardinalityConfig<OptRequired, OptRequired>>(
|
|
170
|
+
config?: C
|
|
171
|
+
) => ConfiguredMap<
|
|
172
|
+
AsFinalizedConfig<
|
|
173
|
+
C, //
|
|
174
|
+
DefaultOneToOneMapping
|
|
175
|
+
>
|
|
176
|
+
>;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* **manyToOne** _mapping_
|
|
180
|
+
*
|
|
181
|
+
* Provides a configuration where multiple inputs `I[]` will be mapped to a
|
|
182
|
+
* single output `O`.
|
|
183
|
+
*
|
|
184
|
+
* Choosing this configuration will, by default, set both input and output
|
|
185
|
+
* to be "required" but you can change this default if you so choose.
|
|
186
|
+
*/
|
|
187
|
+
manyToOne: <
|
|
188
|
+
C extends MapCardinalityConfig<
|
|
189
|
+
OptRequired | undefined, //
|
|
190
|
+
OptRequired | undefined
|
|
191
|
+
>
|
|
192
|
+
>(
|
|
193
|
+
config?: C
|
|
194
|
+
) => ConfiguredMap<
|
|
195
|
+
AsFinalizedConfig<
|
|
196
|
+
C, //
|
|
197
|
+
DefaultManyToOneMapping
|
|
198
|
+
>
|
|
199
|
+
>;
|
|
200
|
+
|
|
201
|
+
oneToMany: <
|
|
202
|
+
C extends MapCardinalityConfig<
|
|
203
|
+
OptRequired | undefined, //
|
|
204
|
+
OptRequired | undefined
|
|
205
|
+
>
|
|
206
|
+
>(
|
|
207
|
+
config?: C
|
|
208
|
+
) => ConfiguredMap<
|
|
209
|
+
AsFinalizedConfig<
|
|
210
|
+
C, //
|
|
211
|
+
DefaultOneToManyMapping
|
|
212
|
+
>
|
|
213
|
+
>;
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
export type MapInput<
|
|
217
|
+
I, //
|
|
218
|
+
IR extends OptRequired,
|
|
219
|
+
C extends MapCardinalityIllustrated
|
|
220
|
+
> = C extends MapCardinality.OneToMany | "I -> O[]"
|
|
21
221
|
? IR extends "opt"
|
|
22
222
|
? I | undefined
|
|
23
223
|
: I
|
|
24
|
-
:
|
|
224
|
+
: C extends MapCardinality.OneToOne | "I -> O"
|
|
25
225
|
? IR extends "opt"
|
|
26
226
|
? I | undefined
|
|
27
227
|
: I
|
|
28
|
-
:
|
|
228
|
+
: C extends MapCardinality.ManyToOne | "I[] -> O"
|
|
29
229
|
? IR extends "opt"
|
|
30
230
|
? I[] | undefined
|
|
31
231
|
: I[]
|
|
32
232
|
: never;
|
|
33
233
|
|
|
34
|
-
export type MapOutput<
|
|
35
|
-
|
|
36
|
-
|
|
234
|
+
export type MapOutput<
|
|
235
|
+
O, //
|
|
236
|
+
OR extends OptRequired,
|
|
237
|
+
C extends MapCardinalityIllustrated
|
|
238
|
+
> = C extends MapCardinality.OneToMany | "I -> O[]"
|
|
37
239
|
? OR extends "opt"
|
|
38
240
|
? O[]
|
|
39
241
|
: [O, ...O[]]
|
|
40
|
-
:
|
|
242
|
+
: C extends MapCardinality.OneToOne | "I -> O"
|
|
41
243
|
? OR extends "opt"
|
|
42
244
|
? O | null
|
|
43
245
|
: O
|
|
44
|
-
:
|
|
246
|
+
: C extends MapCardinality.ManyToOne | "I[] -> O"
|
|
45
247
|
? OR extends "opt"
|
|
46
248
|
? O | null
|
|
47
249
|
: O
|
|
@@ -50,7 +252,8 @@ export type MapOutput<O, OR, D extends MapDirectionVal> = D extends
|
|
|
50
252
|
/**
|
|
51
253
|
* **MapTo<I, O>**
|
|
52
254
|
*
|
|
53
|
-
* A mapping function between an input type `I` and output type `O`.
|
|
255
|
+
* A mapping function between an input type `I` and output type `O`. Defaults to using
|
|
256
|
+
* the _default_ OneToMany mapping config.
|
|
54
257
|
*
|
|
55
258
|
* **Note:** this type is designed to guide the userland mapping; refer
|
|
56
259
|
* to `MapFn` if you want the type output by the `mapFn()` utility.
|
|
@@ -58,16 +261,22 @@ export type MapOutput<O, OR, D extends MapDirectionVal> = D extends
|
|
|
58
261
|
export type MapTo<
|
|
59
262
|
I,
|
|
60
263
|
O,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
264
|
+
C extends FinalizedMapConfig<
|
|
265
|
+
OptRequired,
|
|
266
|
+
MapCardinalityIllustrated,
|
|
267
|
+
OptRequired
|
|
268
|
+
> = DefaultOneToManyMapping
|
|
269
|
+
> = MapIR<C> extends "opt"
|
|
270
|
+
? (source?: MapInput<I, MapIR<C>, MapCard<C>>) => MapOutput<O, MapOR<C>, MapCard<C>>
|
|
271
|
+
: (source: MapInput<I, MapIR<C>, MapCard<C>>) => MapOutput<O, MapOR<C>, MapCard<C>>;
|
|
272
|
+
|
|
273
|
+
export type MapFnOutput<
|
|
274
|
+
I,
|
|
275
|
+
O,
|
|
276
|
+
S,
|
|
277
|
+
OR extends OptRequired,
|
|
278
|
+
C extends MapCardinalityIllustrated
|
|
279
|
+
> = C extends "I -> O[]" | MapCardinality.OneToMany
|
|
71
280
|
? S extends I[]
|
|
72
281
|
? OR extends "opt"
|
|
73
282
|
? O[]
|
|
@@ -75,7 +284,7 @@ export type MapFnOutput<I, O, S, OR extends OptRequired, D extends MapDirectionV
|
|
|
75
284
|
: OR extends "opt"
|
|
76
285
|
? O[] | null
|
|
77
286
|
: O[]
|
|
78
|
-
:
|
|
287
|
+
: C extends MapCardinality.OneToOne | "I -> O"
|
|
79
288
|
? S extends I[]
|
|
80
289
|
? OR extends "opt"
|
|
81
290
|
? O[]
|
|
@@ -83,7 +292,7 @@ export type MapFnOutput<I, O, S, OR extends OptRequired, D extends MapDirectionV
|
|
|
83
292
|
: OR extends "opt"
|
|
84
293
|
? O | null
|
|
85
294
|
: O
|
|
86
|
-
:
|
|
295
|
+
: C extends MapCardinality.ManyToOne | "I[] -> O"
|
|
87
296
|
? S extends I[][]
|
|
88
297
|
? OR extends "opt"
|
|
89
298
|
? O[]
|
|
@@ -93,17 +302,17 @@ export type MapFnOutput<I, O, S, OR extends OptRequired, D extends MapDirectionV
|
|
|
93
302
|
: O
|
|
94
303
|
: never;
|
|
95
304
|
|
|
96
|
-
export type MapFnInput<I, IR extends OptRequired, D extends
|
|
305
|
+
export type MapFnInput<I, IR extends OptRequired, D extends MapCardinalityIllustrated> = D extends
|
|
97
306
|
| "I -> O[]"
|
|
98
|
-
|
|
|
307
|
+
| MapCardinality.OneToMany
|
|
99
308
|
? IR extends "opt"
|
|
100
309
|
? I | I[] | undefined
|
|
101
310
|
: I | I[]
|
|
102
|
-
: D extends
|
|
311
|
+
: D extends MapCardinality.OneToOne | "I -> O"
|
|
103
312
|
? IR extends "opt"
|
|
104
313
|
? I | I[] | undefined
|
|
105
314
|
: I | I[]
|
|
106
|
-
: D extends
|
|
315
|
+
: D extends MapCardinality.ManyToOne | "I[] -> O"
|
|
107
316
|
? IR extends "opt"
|
|
108
317
|
? I[] | I[][] | undefined
|
|
109
318
|
: I[] | I[][]
|
|
@@ -121,16 +330,18 @@ export type MapFnInput<I, IR extends OptRequired, D extends MapDirectionVal> = D
|
|
|
121
330
|
* ```
|
|
122
331
|
* 2. Block:
|
|
123
332
|
* ```ts
|
|
124
|
-
* // maps inputs to outputs (filtering or splitting where
|
|
333
|
+
* // maps inputs to outputs (filtering or splitting where appr.)
|
|
125
334
|
* const out2 = m(inputs);
|
|
126
335
|
* ```
|
|
127
336
|
*/
|
|
128
337
|
export type MapFn<
|
|
129
338
|
I,
|
|
130
339
|
O,
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
: <S extends MapFnInput<I,
|
|
340
|
+
C extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>
|
|
341
|
+
> = MapIR<C> extends "opt"
|
|
342
|
+
? <S extends MapFnInput<I, MapIR<C>, MapCard<C>>>(
|
|
343
|
+
source?: S
|
|
344
|
+
) => MapFnOutput<I, O, S, MapOR<C>, MapCard<C>>
|
|
345
|
+
: <S extends MapFnInput<I, MapIR<C>, MapCard<C>>>(
|
|
346
|
+
source: S
|
|
347
|
+
) => MapFnOutput<I, O, S, MapOR<C>, MapCard<C>>;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Alpha } from "../alphabetic/alpha-characters";
|
|
2
|
+
import { Keys } from "../Keys";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Extracts the _required_ keys in the object's type. You also may
|
|
@@ -12,6 +13,16 @@ export type RequiredKeys<T extends object, V extends any = any> = {
|
|
|
12
13
|
: never;
|
|
13
14
|
}[keyof T];
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Extracts the intersecting/common keys to two objects
|
|
18
|
+
*/
|
|
19
|
+
export type IntersectingKeys<
|
|
20
|
+
T extends Record<string, any> | readonly string[],
|
|
21
|
+
U extends Record<string, any> | readonly string[]
|
|
22
|
+
> = {
|
|
23
|
+
[K in keyof T]: K extends Keys<U> ? K : never;
|
|
24
|
+
}[keyof T];
|
|
25
|
+
|
|
15
26
|
/**
|
|
16
27
|
* Extracts the _optional_ keys in the object's type. You also may
|
|
17
28
|
* optionally filter by the _value_ of the key.
|
|
@@ -8,10 +8,7 @@ export type ExpectExtends<VALUE, EXPECTED> = EXPECTED extends VALUE ? true : fal
|
|
|
8
8
|
*/
|
|
9
9
|
export type AssertExtends<VALUE, EXPECTED> = EXPECTED extends VALUE ? any : never;
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
export type IfExtends<VALUE, EXPECTED> = EXPECTED extends VALUE ? VALUE : never;
|
|
13
|
-
|
|
14
11
|
/**
|
|
15
|
-
* Give a type `TValue` and a comparison type `TExtends`
|
|
12
|
+
* Give a type `TValue` and a comparison type `TExtends`
|
|
16
13
|
*/
|
|
17
|
-
export type IfExtendsThen<VALUE, EXPECTED, THEN> = EXPECTED extends VALUE ? THEN : never;
|
|
14
|
+
export type IfExtendsThen<VALUE, EXPECTED, THEN> = EXPECTED extends VALUE ? THEN : never;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DictFromKv } from "src/types/kv";
|
|
2
|
-
import { Mutable } from "src/types/Mutable";
|
|
3
2
|
import { Narrowable } from "src/types/Narrowable";
|
|
4
3
|
|
|
5
4
|
/**
|
|
@@ -21,5 +20,5 @@ export function kvToDict<
|
|
|
21
20
|
out[kv.key as keyof DictFromKv<T>] = kv.value;
|
|
22
21
|
}
|
|
23
22
|
|
|
24
|
-
return out as DictFromKv<
|
|
23
|
+
return out as DictFromKv<T>;
|
|
25
24
|
}
|
|
@@ -1,118 +1,155 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
FinalizedMapConfig,
|
|
3
|
+
MapCardinalityIllustrated,
|
|
4
|
+
MapFnInput,
|
|
5
|
+
MapperApi,
|
|
6
|
+
MapTo,
|
|
7
|
+
AsFinalizedConfig,
|
|
8
|
+
MapConfig,
|
|
9
|
+
ConfiguredMap,
|
|
10
|
+
MapFnOutput,
|
|
11
|
+
MapIR,
|
|
12
|
+
MapCard,
|
|
13
|
+
MapOR,
|
|
14
|
+
} from "src/types/dictionary";
|
|
2
15
|
import { OptRequired } from "src/types/literal-unions";
|
|
3
16
|
import { createFnWithProps } from "../createFnWithProps";
|
|
4
17
|
|
|
5
|
-
export interface MapConfig<
|
|
6
|
-
IR extends OptRequired = "req",
|
|
7
|
-
D extends MapDirectionVal = "I -> O[]",
|
|
8
|
-
OR extends OptRequired = "opt"
|
|
9
|
-
> {
|
|
10
|
-
input?: IR;
|
|
11
|
-
output?: OR;
|
|
12
|
-
direction?: D;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
18
|
/**
|
|
16
|
-
*
|
|
19
|
+
* utility function to take a fully-qualified _user_ config
|
|
20
|
+
* and make it into a FinalizedMapConfig
|
|
17
21
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
* prior to providing a mapping function.
|
|
23
|
-
*/
|
|
24
|
-
export type MapperApiConfigured = <
|
|
25
|
-
IR extends OptRequired = "req",
|
|
26
|
-
D extends MapDirectionVal = "I -> O[]",
|
|
27
|
-
OR extends OptRequired = "opt"
|
|
22
|
+
const toFinalizedConfig = <
|
|
23
|
+
IR extends OptRequired,
|
|
24
|
+
D extends MapCardinalityIllustrated,
|
|
25
|
+
OR extends OptRequired
|
|
28
26
|
>(
|
|
29
27
|
config: MapConfig<IR, D, OR>
|
|
30
|
-
) =>
|
|
28
|
+
) => {
|
|
29
|
+
return { ...config, finalized: true } as FinalizedMapConfig<IR, D, OR>;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const DEFAULT_ONE_TO_MANY_MAPPING = toFinalizedConfig({
|
|
33
|
+
input: "req",
|
|
34
|
+
output: "opt",
|
|
35
|
+
cardinality: "I -> O[]",
|
|
36
|
+
});
|
|
37
|
+
export const DEFAULT_ONE_TO_ONE_MAPPING = toFinalizedConfig({
|
|
38
|
+
input: "req",
|
|
39
|
+
output: "req",
|
|
40
|
+
cardinality: "I -> O",
|
|
41
|
+
});
|
|
42
|
+
export const DEFAULT_MANY_TO_ONE_MAPPING = toFinalizedConfig({
|
|
43
|
+
input: "req",
|
|
44
|
+
output: "req",
|
|
45
|
+
cardinality: "I[] -> O",
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export type DefaultOneToManyMapping = typeof DEFAULT_ONE_TO_MANY_MAPPING;
|
|
49
|
+
export type DefaultOneToOneMapping = typeof DEFAULT_ONE_TO_ONE_MAPPING;
|
|
50
|
+
export type DefaultManyToOneMapping = typeof DEFAULT_MANY_TO_ONE_MAPPING;
|
|
31
51
|
|
|
32
52
|
/**
|
|
33
53
|
* The single implementation for all mapping
|
|
34
54
|
*/
|
|
35
55
|
const mapper =
|
|
36
|
-
<
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
// TODO
|
|
45
|
-
config = {
|
|
46
|
-
input: "req",
|
|
47
|
-
output: "opt",
|
|
48
|
-
direction: "I -> O[]",
|
|
49
|
-
...config,
|
|
50
|
-
} as Required<MapConfig<IR, D, OR>>;
|
|
51
|
-
|
|
52
|
-
const fn = <S extends MapFnInput<I, IR, D>>(source?: S) => {
|
|
56
|
+
<C extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>>(config: C) =>
|
|
57
|
+
<I, O>(map: MapTo<I, O, C>) => {
|
|
58
|
+
return <S extends MapFnInput<I, MapIR<C>, MapCard<C>>>(
|
|
59
|
+
source?: S
|
|
60
|
+
): MapFnOutput<I, O, S, MapOR<C>, MapCard<C>> => {
|
|
61
|
+
/**
|
|
62
|
+
* Determine whether input is an array; this will be true
|
|
63
|
+
*/
|
|
53
64
|
const isArray =
|
|
54
|
-
config.
|
|
65
|
+
config.cardinality === "I -> O[]" && Array.isArray(source)
|
|
55
66
|
? true
|
|
56
|
-
: config.
|
|
67
|
+
: config.cardinality === "I[] -> O" && Array.isArray(source) && Array.isArray(source[0])
|
|
57
68
|
? true
|
|
58
69
|
: false;
|
|
59
70
|
|
|
60
71
|
if (isArray) {
|
|
61
|
-
return
|
|
72
|
+
// iterate over inputs with flatMap to return mapped values as
|
|
73
|
+
// well as supporting filtering functionality. We could achieve
|
|
74
|
+
// the same results by simply passing in all inputs to our mapper
|
|
75
|
+
// in most cardinalities but when cardinality is M:1 we need to
|
|
76
|
+
// make sure that the first array of elements is passed as a single
|
|
77
|
+
// item and this approach achieves this.
|
|
78
|
+
// TODO: we should check that the approach below doesn't work for M:1 here
|
|
79
|
+
// as well
|
|
80
|
+
return (source as any).flatMap(map) as MapFnOutput<I, O, S, MapOR<C>, MapCard<C>>;
|
|
62
81
|
} else {
|
|
63
|
-
|
|
82
|
+
// receive _all_ inputs provided as pass into ; this is just a single input unless the
|
|
83
|
+
// cardinality is
|
|
84
|
+
return map(source as any) as MapFnOutput<I, O, S, MapOR<C>, MapCard<C>>;
|
|
64
85
|
}
|
|
65
86
|
};
|
|
66
|
-
|
|
67
|
-
return fn as unknown as MapFn<I, O, IR, D, OR>;
|
|
68
87
|
};
|
|
69
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Takes a userland configuration and a default configuration and then produces
|
|
91
|
+
* the appropriately typed `ConfiguredMap` API surface.
|
|
92
|
+
*/
|
|
93
|
+
const setMapper = <
|
|
94
|
+
U extends MapConfig<
|
|
95
|
+
OptRequired | undefined,
|
|
96
|
+
MapCardinalityIllustrated | undefined,
|
|
97
|
+
OptRequired | undefined
|
|
98
|
+
>,
|
|
99
|
+
D extends FinalizedMapConfig<OptRequired, MapCardinalityIllustrated, OptRequired>
|
|
100
|
+
>(
|
|
101
|
+
config: U = { input: undefined, output: undefined, cardinality: undefined } as U,
|
|
102
|
+
defaultValue: D
|
|
103
|
+
): ConfiguredMap<AsFinalizedConfig<U, D>> => ({
|
|
104
|
+
map: (source) => {
|
|
105
|
+
// merge userland config with defaults for a cardinality
|
|
106
|
+
const c = {
|
|
107
|
+
...defaultValue,
|
|
108
|
+
...config,
|
|
109
|
+
} as unknown as AsFinalizedConfig<U, D>;
|
|
110
|
+
return mapper(c)(source);
|
|
111
|
+
},
|
|
112
|
+
input: config?.input || defaultValue.input,
|
|
113
|
+
output: config?.output || defaultValue.output,
|
|
114
|
+
cardinality: config?.cardinality || defaultValue.cardinality,
|
|
115
|
+
});
|
|
116
|
+
|
|
70
117
|
/**
|
|
71
118
|
* **mapTo** _utility_
|
|
72
119
|
*
|
|
73
|
-
* This utility creates a strongly typed data mapper which maps from one
|
|
74
|
-
* known source `I` to another `O`:
|
|
120
|
+
* This utility -- by default -- creates a strongly typed 1:M data mapper which maps from one
|
|
121
|
+
* known source `I` to any array of another `O[]`:
|
|
75
122
|
* ```ts
|
|
76
123
|
* const mapper = mapTo<I, O>(i => [{
|
|
77
124
|
* foo: i.bar
|
|
78
125
|
* }]);
|
|
79
126
|
* ```
|
|
80
127
|
*/
|
|
81
|
-
export const mapToFn
|
|
82
|
-
return mapper()
|
|
128
|
+
export const mapToFn: ConfiguredMap<DefaultOneToManyMapping>["map"] = (map) => {
|
|
129
|
+
return mapper(DEFAULT_ONE_TO_MANY_MAPPING)(map);
|
|
83
130
|
};
|
|
84
131
|
|
|
85
132
|
/**
|
|
86
133
|
* Provides a `config` method which allows the relationships between _inputs_
|
|
87
134
|
* and _outputs_ to be configured.
|
|
88
135
|
*/
|
|
89
|
-
export const mapToDict = {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
c
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
* .config({ ... })
|
|
107
|
-
* .map<I, O>(i => [{
|
|
108
|
-
* foo: i.bar
|
|
109
|
-
* }]);
|
|
110
|
-
* ```
|
|
111
|
-
*/
|
|
112
|
-
map<I, O>(map: MapTo<I, O, IR, D, OR>): MapFn<I, O, IR, D, OR> {
|
|
113
|
-
return mapper(c)(map);
|
|
114
|
-
},
|
|
115
|
-
}),
|
|
136
|
+
export const mapToDict: MapperApi = {
|
|
137
|
+
config(config) {
|
|
138
|
+
const c = { ...DEFAULT_ONE_TO_MANY_MAPPING, ...config };
|
|
139
|
+
return setMapper(c, DEFAULT_ONE_TO_MANY_MAPPING);
|
|
140
|
+
},
|
|
141
|
+
oneToOne(config) {
|
|
142
|
+
const c = { ...DEFAULT_ONE_TO_ONE_MAPPING, ...config };
|
|
143
|
+
return setMapper(c, DEFAULT_ONE_TO_ONE_MAPPING);
|
|
144
|
+
},
|
|
145
|
+
manyToOne(config) {
|
|
146
|
+
const c = { ...DEFAULT_MANY_TO_ONE_MAPPING, ...config };
|
|
147
|
+
return setMapper(c, DEFAULT_MANY_TO_ONE_MAPPING);
|
|
148
|
+
},
|
|
149
|
+
oneToMany(config) {
|
|
150
|
+
const c = { ...DEFAULT_ONE_TO_MANY_MAPPING, ...config };
|
|
151
|
+
return setMapper(c, DEFAULT_ONE_TO_MANY_MAPPING);
|
|
152
|
+
},
|
|
116
153
|
};
|
|
117
154
|
|
|
118
155
|
/**
|
|
@@ -120,5 +157,13 @@ export const mapToDict = {
|
|
|
120
157
|
*
|
|
121
158
|
* This utility creates a strongly typed data mapper which maps from one
|
|
122
159
|
* known source `I` to another `O`.
|
|
160
|
+
*
|
|
161
|
+
* Signatures:
|
|
162
|
+
* ```ts
|
|
163
|
+
* const defMap = mapTo<I,O>( ... );
|
|
164
|
+
* const configured = mapTo.config({ output: "req" }).map<I,O>( ... );
|
|
165
|
+
* const one2one = mapTo.oneToOne().map<I,O>( ... );
|
|
166
|
+
* const many2one = mapTo.manyToOne().map<I,O>( ... );
|
|
167
|
+
* ```
|
|
123
168
|
*/
|
|
124
169
|
export const mapTo = createFnWithProps(mapToFn, mapToDict);
|