inferred-types 0.25.0 → 0.27.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.
@@ -0,0 +1,80 @@
1
+ import { UnionToTuple } from "../type-conversion";
2
+
3
+ export type OneToOne = `1:1`;
4
+ export type OneToMany = `1:M`;
5
+ export type OneToZero = `1:0`;
6
+ export type ZeroToOne = `0:1`;
7
+ export type ZeroToMany = `0:M`;
8
+ export type ZeroToZero = `0:0`;
9
+ export type ManyToMany = "M:M";
10
+ export type ManyToOne = "M:1";
11
+ export type ManyToZero = "M:0";
12
+
13
+ export type CardinalityNode = "0" | "1" | "M";
14
+
15
+ /**
16
+ * Cardinality which expects a singular input and requires
17
+ * 1 or many outputs.
18
+ *
19
+ * Note: choose `CardinalityFilter1` if you want to allow output
20
+ * to have no outputs.
21
+ */
22
+ export type Cardinality1 = OneToOne | OneToMany;
23
+
24
+ /**
25
+ * Cardinality which expects a singular input and maps to 0,
26
+ * 1, or many outputs.
27
+ */
28
+ export type CardinalityFilter1 = OneToOne | OneToMany | OneToZero;
29
+
30
+ /**
31
+ * Cardinality which expects a singular input which is allowed to be
32
+ * _undefined_ or the expected type.
33
+ */
34
+ export type Cardinality0 = ZeroToOne | ZeroToMany | OneToOne | OneToMany;
35
+
36
+ /**
37
+ * Cardinality which expects a singular input -- which is allowed to be
38
+ * _undefined_ -- and maps to 0,
39
+ * 1, or many outputs.
40
+ */
41
+ export type CardinalityFilter0 =
42
+ | ZeroToOne
43
+ | ZeroToMany
44
+ | OneToOne
45
+ | OneToMany
46
+ | OneToZero
47
+ | ZeroToZero;
48
+
49
+ export type CardinalityExplicit = `${number}:${number}`;
50
+
51
+ /**
52
+ * Cardinality of any sort between two types
53
+ */
54
+ export type Cardinality =
55
+ | CardinalityFilter0
56
+ | CardinalityFilter1
57
+ | ManyToMany
58
+ | ManyToOne
59
+ | ManyToZero
60
+ | CardinalityExplicit;
61
+
62
+ export type CardinalityTuple<T extends Cardinality> = UnionToTuple<T>;
63
+
64
+ /**
65
+ * The first or _input_ part of the Cardinality relationship
66
+ */
67
+ export type CardinalityIn<T extends Cardinality> = T extends `${infer IN}:${string}` ? IN : never;
68
+
69
+ /**
70
+ * The second or _output_ part of the Cardinality relationship
71
+ */
72
+ export type CardinalityOut<T extends Cardinality> = T extends `${string}:${infer OUT}`
73
+ ? OUT
74
+ : never;
75
+
76
+ export type CardinalityInput<T, C extends Cardinality> = CardinalityTuple<C>[0] extends 0
77
+ ? T | undefined
78
+ : CardinalityTuple<C>[0] extends 1
79
+ ? T
80
+ : T[];
@@ -9,6 +9,7 @@ export * from "./CamelCase";
9
9
  export * from "./CapitalizeWords";
10
10
  export * from "./DashToSnake";
11
11
  export * from "./DashUppercase";
12
+ export * from "./Cardinality";
12
13
  export * from "./Dasherize";
13
14
  export * from "./HasUppercase";
14
15
  export * from "./IsCapitalized";
@@ -1,23 +1,136 @@
1
+ import { OptRequired } from "src/types/literal-unions";
2
+ import { EnumValues } from "../EnumValues";
3
+
1
4
  /**
2
- * **MapTo**
3
- *
4
- * Maps from one type `I` to another `O[]`
5
- *
6
- * **Note:** because the output is an array you can easily support `1:1` and `1:M` mappings
7
- * but not a filtering operation (e.g., `1:0`); if you need this then use `MapToWithFiltering`
8
- * instead.
5
+ * Expresses relationship between inputs/outputs:
9
6
  */
10
- export type MapTo<I extends {}, O extends {}> = (i: I) => O[];
7
+ export enum MapDirection {
8
+ /** every input results in 0:M outputs */
9
+ OneToMany = "I -> O[]",
10
+ /** every input results in 0:1 outputs */
11
+ OneToOne = "I -> O",
12
+ /** every input is an array of type I and reduced to a single O */
13
+ ManyToOne = "I[] -> O",
14
+ }
15
+
16
+ export type MapDirectionVal = EnumValues<MapDirection>;
17
+
18
+ export type MapInput<I, IR, D extends MapDirectionVal> = D extends
19
+ | MapDirection.OneToMany
20
+ | "I -> O[]"
21
+ ? IR extends "opt"
22
+ ? I | undefined
23
+ : I
24
+ : D extends MapDirection.OneToOne | "I -> O"
25
+ ? IR extends "opt"
26
+ ? I | undefined
27
+ : I
28
+ : D extends MapDirection.ManyToOne | "I[] -> O"
29
+ ? IR extends "opt"
30
+ ? I[] | undefined
31
+ : I[]
32
+ : never;
33
+
34
+ export type MapOutput<O, OR, D extends MapDirectionVal> = D extends
35
+ | MapDirection.OneToMany
36
+ | "I -> O[]"
37
+ ? OR extends "opt"
38
+ ? O[]
39
+ : [O, ...O[]]
40
+ : D extends MapDirection.OneToOne | "I -> O"
41
+ ? OR extends "opt"
42
+ ? O | null
43
+ : O
44
+ : D extends MapDirection.ManyToOne | "I[] -> O"
45
+ ? OR extends "opt"
46
+ ? O | null
47
+ : O
48
+ : never;
11
49
 
12
50
  /**
13
- * **MapToWithFiltering**
51
+ * **MapTo<I, O>**
14
52
  *
15
- * Maps from one type `I` to another `(O | null)[]`
53
+ * A mapping function between an input type `I` and output type `O`.
16
54
  *
17
- * **Note:** because the output is an array you can easily support `1:1` and `1:M` mappings
18
- * and the allowance of the conversion to result in a `null` value also means that filter
19
- * out an input value entirely is possible. If you don't need _filtering_ then use the
20
- * `MapTo` type instead.
55
+ * **Note:** this type is designed to guide the userland mapping; refer
56
+ * to `MapFn` if you want the type output by the `mapFn()` utility.
21
57
  */
58
+ export type MapTo<
59
+ I,
60
+ O,
61
+ IR extends OptRequired = "req",
62
+ D extends MapDirectionVal = MapDirection.OneToMany,
63
+ OR extends OptRequired = "opt"
64
+ > = IR extends "opt"
65
+ ? (source?: MapInput<I, IR, D>) => MapOutput<O, OR, D>
66
+ : (source: MapInput<I, IR, D>) => MapOutput<O, OR, D>;
22
67
 
23
- export type MapToWithFiltering<I extends {}, O extends {}> = (i: I) => O[] | null;
68
+ export type MapFnOutput<I, O, S, OR extends OptRequired, D extends MapDirectionVal> = D extends
69
+ | "I -> O[]"
70
+ | MapDirection.OneToMany
71
+ ? S extends I[]
72
+ ? OR extends "opt"
73
+ ? O[]
74
+ : [O, ...O[]]
75
+ : OR extends "opt"
76
+ ? O[] | null
77
+ : O[]
78
+ : D extends MapDirection.OneToOne | "I -> O"
79
+ ? S extends I[]
80
+ ? OR extends "opt"
81
+ ? O[]
82
+ : [O, ...O[]]
83
+ : OR extends "opt"
84
+ ? O | null
85
+ : O
86
+ : D extends MapDirection.ManyToOne | "I[] -> O"
87
+ ? S extends I[][]
88
+ ? OR extends "opt"
89
+ ? O[]
90
+ : [O, ...O[]]
91
+ : OR extends "opt"
92
+ ? O | null
93
+ : O
94
+ : never;
95
+
96
+ export type MapFnInput<I, IR extends OptRequired, D extends MapDirectionVal> = D extends
97
+ | "I -> O[]"
98
+ | MapDirection.OneToMany
99
+ ? IR extends "opt"
100
+ ? I | I[] | undefined
101
+ : I | I[]
102
+ : D extends MapDirection.OneToOne | "I -> O"
103
+ ? IR extends "opt"
104
+ ? I | I[] | undefined
105
+ : I | I[]
106
+ : D extends MapDirection.ManyToOne | "I[] -> O"
107
+ ? IR extends "opt"
108
+ ? I[] | I[][] | undefined
109
+ : I[] | I[][]
110
+ : never;
111
+
112
+ /**
113
+ * The mapping function provided by the `mapFn()` utility. This _fn_
114
+ * is intended to be used in two ways:
115
+ *
116
+ * 1. Iterative:
117
+ * ```ts
118
+ * const m = mapTo<I,O>(i => [ ... ]);
119
+ * // maps inputs to outputs
120
+ * const out = inputs.map(m);
121
+ * ```
122
+ * 2. Block:
123
+ * ```ts
124
+ * // maps inputs to outputs (filtering or splitting where approp)
125
+ * const out2 = m(inputs);
126
+ * ```
127
+ */
128
+ export type MapFn<
129
+ I,
130
+ O,
131
+ IR extends OptRequired = "req",
132
+ D extends MapDirectionVal = MapDirection.OneToMany,
133
+ OR extends OptRequired = "opt"
134
+ > = IR extends "opt"
135
+ ? <S extends MapFnInput<I, IR, D>>(source?: S) => MapFnOutput<I, O, S, OR, D>
136
+ : <S extends MapFnInput<I, IR, D>>(source: S) => MapFnOutput<I, O, S, OR, D>;
@@ -0,0 +1,4 @@
1
+ /**
2
+ * A function which returns a boolean value
3
+ */
4
+ export type LogicFunction<T extends any[]> = (...args: T) => boolean;
@@ -5,6 +5,7 @@
5
5
 
6
6
  // file exports
7
7
  export * from "./FinalReturn";
8
+ export * from "./LogicFunction";
8
9
 
9
10
  // #endregion auto-indexed files
10
11
 
@@ -37,6 +37,7 @@ export * from "./fluent/index";
37
37
  export * from "./functions/index";
38
38
  export * from "./kv/index";
39
39
  export * from "./lists/index";
40
+ export * from "./literal-unions/index";
40
41
  export * from "./string-literals/index";
41
42
  export * from "./tuples/index";
42
43
  export * from "./type-conversion/index";
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Expresses whether an option is "opt" (optional) or "req" (required)
3
+ */
4
+ export type OptRequired = "opt" | "req";
@@ -0,0 +1 @@
1
+ export * from "./OptRequired";
@@ -5,9 +5,13 @@
5
5
  * // "Foo"
6
6
  * type Foo = Replace<typeof fooy, "y", "">;
7
7
  * ```
8
- *
8
+ *
9
9
  * Note: _the first match is replaced and all subsequent matches are ignored_
10
10
  */
11
- export type Replace<S extends string, W extends string, P extends string> =
12
- S extends "" ? "" : W extends "" ? S :
13
- S extends `${infer F}${W}${infer E}` ? `${F}${P}${E}` : S;
11
+ export type Replace<S extends string, W extends string, P extends string> = S extends ""
12
+ ? ""
13
+ : W extends ""
14
+ ? S
15
+ : S extends `${infer F}${W}${infer E}`
16
+ ? `${F}${P}${E}`
17
+ : S;
@@ -0,0 +1,15 @@
1
+ import { LogicFunction } from "src/types/functions";
2
+
3
+ /**
4
+ * Groups a number of "logic functions" together by combining their results using
5
+ * the logical **AND** operator.
6
+ *
7
+ * **Note:** a "logic function" is any function which returns a boolean
8
+ */
9
+ export const and = <T extends any[]>(...ops: readonly LogicFunction<T>[]): LogicFunction<T> => {
10
+ const fn: LogicFunction<T> = (...args: T) => {
11
+ return [...ops].every((i) => i(...args));
12
+ };
13
+
14
+ return fn;
15
+ };
@@ -0,0 +1,268 @@
1
+ import { asArray } from "../lists/asArray";
2
+
3
+ // string filters
4
+ export type FilterStarts = {
5
+ /** one or more string which the value is allowed to start with */
6
+ startsWith: string | string[];
7
+ };
8
+ export type FilterIs = {
9
+ /** whether a string _**is**_ of a particular value */
10
+ is: string | string[];
11
+ };
12
+ export type FilterEnds = { endsWith: string | string[] };
13
+ export type FilterContains = {
14
+ /** whether any of the strings specified are _contained_ in the value */
15
+ contains: string | string[];
16
+ };
17
+
18
+ // numeric filters
19
+ export type FilterEquals = {
20
+ /** one or more values which _equal_ the value passed in */
21
+ equals: number | number[];
22
+ };
23
+ export type FilterNotEqual = {
24
+ /** one or more values which ALL _do not equal_ the value passed in */
25
+ notEqual: number | number[];
26
+ };
27
+ export type FilterGreaterThan = {
28
+ /** the incoming value is greater than this value */
29
+ greaterThan: number;
30
+ };
31
+ export type FilterLessThan = {
32
+ /** the incoming value is less than this value */
33
+ lessThan: number;
34
+ };
35
+
36
+ export type StringFilter =
37
+ | FilterIs
38
+ | FilterStarts
39
+ | FilterEnds
40
+ | FilterContains
41
+ | (FilterStarts & FilterEnds)
42
+ | (FilterStarts & FilterContains)
43
+ | (FilterEnds & FilterContains)
44
+ | (FilterStarts & FilterEnds & FilterContains);
45
+
46
+ export type NumericFilter =
47
+ | FilterEquals
48
+ | FilterNotEqual
49
+ | FilterGreaterThan
50
+ | FilterLessThan
51
+ | (FilterEquals & FilterNotEqual)
52
+ | (FilterEquals & FilterGreaterThan)
53
+ | (FilterEquals & FilterLessThan)
54
+ | (FilterNotEqual & FilterGreaterThan)
55
+ | (FilterNotEqual & FilterLessThan)
56
+ | (FilterGreaterThan & FilterLessThan)
57
+ | (FilterEquals & FilterGreaterThan & FilterLessThan)
58
+ | (FilterNotEqual & FilterGreaterThan & FilterLessThan)
59
+ | (FilterEquals & FilterNotEqual & FilterGreaterThan & FilterLessThan);
60
+
61
+ export type FilterDefn = StringFilter | NumericFilter;
62
+
63
+ // NOT filter
64
+ export type NotFilter = {
65
+ /**
66
+ * **not**
67
+ *
68
+ * If you want to build a filter who's conditions being met results in _filtering_
69
+ * the value rather than accepting it then choose this.
70
+ */
71
+ not: FilterDefn;
72
+ };
73
+
74
+ export function isNotFilter(f: FilterDefn | NotFilter): f is NotFilter {
75
+ return typeof f === "object" && "not" in f;
76
+ }
77
+
78
+ export type UnwrapNot<T extends FilterDefn | NotFilter> = T extends NotFilter
79
+ ? T["not"] extends StringFilter
80
+ ? StringFilter
81
+ : NumericFilter
82
+ : T;
83
+
84
+ export function isNumericFilter(filter: FilterDefn): filter is NumericFilter {
85
+ return "equals" in filter ||
86
+ "notEqual" in filter ||
87
+ "greaterThan" in filter ||
88
+ "lessThan" in filter
89
+ ? true
90
+ : false;
91
+ }
92
+
93
+ export type UndefinedValue<U extends boolean> = true extends U
94
+ ? "undefined treated as 'true'"
95
+ : U extends "no-impact"
96
+ ? "undefined treated in no-impact fashion"
97
+ : "undefined treated as 'false'";
98
+
99
+ export type UndefinedTreatment = "undefined treated as 'true'" | "undefined treated as 'false'";
100
+
101
+ export type LogicalCombinator = "AND" | "OR";
102
+
103
+ /**
104
+ * **FilterFn**
105
+ *
106
+ * A filter function derived from the `filter()` configurator. This function is intended to provide a type-strong _filter_ function which can be used like so:
107
+ * be used like:
108
+ * ```ts
109
+ * const onlyPrivate = filter({ startsWith: "_" });
110
+ * const privateFiles = files.filter(onlyPrivate);
111
+ * ```
112
+ *
113
+ */
114
+ export type FilterFn<T extends StringFilter | NumericFilter> = T extends StringFilter
115
+ ? <V extends string | undefined>(input: V) => boolean
116
+ : <V extends number | undefined>(input: V) => boolean;
117
+
118
+ /**
119
+ * Defines a logical function for each condition type
120
+ */
121
+ export type ConditionFilter<T extends StringFilter | NumericFilter> = T extends StringFilter
122
+ ? (input: string) => boolean
123
+ : (input: number) => boolean;
124
+
125
+ /**
126
+ * Wraps numeric filter's configuration into a logic filter
127
+ */
128
+ const numericOps = <L extends LogicalCombinator>(config: NumericFilter, boolLogic: L) => {
129
+ const equals = (n: number | number[]): [ConditionFilter<NumericFilter>] | [] => {
130
+ const f = asArray(n);
131
+ return f.length === 0 ? [] : [(input?: number) => f.some((i) => i === input)];
132
+ };
133
+ const notEqual = (n: number | number[]): [ConditionFilter<NumericFilter>] | [] => {
134
+ const f = asArray(n);
135
+ return f.length === 0 ? [] : [(input?: number) => f.every((i) => i !== input)];
136
+ };
137
+
138
+ /** returns 1 FilterFn's */
139
+ const greaterThan = <V extends number>(n: V) => {
140
+ const val = [(input?: V) => input !== undefined && input > n] as [(input: V) => boolean];
141
+ return val as [ConditionFilter<NumericFilter>];
142
+ };
143
+
144
+ /** returns 0 or 1 FilterFn's */
145
+ const lessThan = <V extends number>(n: V) => {
146
+ const val = [(input: V) => input !== undefined && input < n];
147
+ return val as [ConditionFilter<NumericFilter>] | [];
148
+ };
149
+
150
+ const conditions = [
151
+ //
152
+ ...("equals" in config ? equals(config.equals) : []),
153
+ ...("notEqual" in config ? notEqual(config.notEqual) : []),
154
+ ...("greaterThan" in config ? greaterThan(config.greaterThan) : []),
155
+ ...("lessThan" in config ? lessThan(config.lessThan) : []),
156
+ ];
157
+
158
+ const combined =
159
+ boolLogic === "AND"
160
+ ? <N extends number>(input: N) => conditions.every((c) => c(input))
161
+ : <N extends number>(input: N) => conditions.some((c) => c(input));
162
+
163
+ return combined;
164
+ };
165
+
166
+ /** wraps configuration along with boolean logic */
167
+ const stringOps = <L extends LogicalCombinator>(
168
+ config: StringFilter,
169
+ boolLogic: L
170
+ ): ConditionFilter<StringFilter> => {
171
+ const startsWith = (n: string | string[]): [ConditionFilter<StringFilter>] | [] => {
172
+ const f = asArray(n);
173
+ return f.length === 0 ? [] : [(input?: string) => f.some((i) => input?.startsWith(i))];
174
+ };
175
+ const endsWith = (n: string | string[]): [ConditionFilter<StringFilter>] | [] => {
176
+ const f = asArray(n);
177
+ return f.length === 0 ? [] : [(input?: string) => f.some((i) => input?.endsWith(i))];
178
+ };
179
+ const contains = (n: string | string[]): [ConditionFilter<StringFilter>] | [] => {
180
+ const f = asArray(n);
181
+ return f.length === 0 ? [] : [(input: string) => f.some((i) => input?.includes(i))];
182
+ };
183
+
184
+ const conditions = [
185
+ ...("startsWith" in config ? startsWith(config.startsWith) : []),
186
+ ...("endsWith" in config ? endsWith(config.endsWith) : []),
187
+ ...("contains" in config ? contains(config.contains) : []),
188
+ ];
189
+
190
+ const combined =
191
+ boolLogic === "AND"
192
+ ? (input: string) => conditions.every((c) => c(input))
193
+ : (input: string) => conditions.some((c) => c(input));
194
+
195
+ return combined as ConditionFilter<StringFilter>;
196
+ };
197
+
198
+ const filterFn = <
199
+ F extends FilterDefn | NotFilter,
200
+ C extends LogicalCombinator,
201
+ U extends boolean | "no-impact"
202
+ >(
203
+ defn: F,
204
+ logicCombinator: C,
205
+ ifUndefined: U = "no-impact" as U
206
+ ): FilterFn<UnwrapNot<F>> => {
207
+ type D = UnwrapNot<F>;
208
+ const config: D = isNotFilter(defn) ? (defn["not"] as D) : (defn as D);
209
+
210
+ const filter: FilterFn<D> = (
211
+ //
212
+ input: Parameters<FilterFn<D>>[0]
213
+ ) => {
214
+ const undefValue =
215
+ ifUndefined === "no-impact"
216
+ ? logicCombinator === "AND"
217
+ ? true
218
+ : false
219
+ : (ifUndefined as true | false);
220
+
221
+ let flag: boolean;
222
+
223
+ if (typeof input === "undefined") {
224
+ flag = undefValue;
225
+ } else if (isNumericFilter(config)) {
226
+ const fn = numericOps(config, logicCombinator);
227
+ flag = isNotFilter(defn) ? !fn(input as number) : fn(input as number);
228
+ } else {
229
+ const fn = stringOps(config, logicCombinator);
230
+ flag = isNotFilter(defn) ? !fn(input as string) : fn(input as string);
231
+ }
232
+
233
+ return flag;
234
+ };
235
+
236
+ return filter;
237
+ };
238
+
239
+ /**
240
+ * **filter**
241
+ *
242
+ * A higher order helper utility which builds a boolean _filter_ function based on a simple
243
+ * configuration object. Support either _string_ or _numeric_ filters.
244
+ *
245
+ * ```ts
246
+ * const str = filter({startsWith: ["_", "."], endsWith: ".md"});
247
+ * const num = filter({ greaterThan: 55, notEqual: [66, 77]});
248
+ * ```
249
+ *
250
+ * All conditions (e.g., `startsWith`, `contains`, `notEqual`, etc.) that a particular filter
251
+ * is defined as having -- if there is more than one -- will be logically combined using AND
252
+ * unless specified otherwise in the third parameter.
253
+ *
254
+ * How a value of _undefined_ will be treated is stated in the second parameter but defaults
255
+ * to "no-impact" which means it's `false` when the logicCombinator is OR but defaults
256
+ * to `true` when the logicCombinator is AND.
257
+ */
258
+ export const filter = <
259
+ F extends FilterDefn | NotFilter,
260
+ U extends boolean | "no-impact",
261
+ C extends LogicalCombinator
262
+ >(
263
+ config: F,
264
+ logicCombinator: C = "AND" as C,
265
+ ifUndefined: U = "no-impact" as U
266
+ ) => {
267
+ return filterFn(config, logicCombinator, ifUndefined);
268
+ };
@@ -0,0 +1,4 @@
1
+ export * from "./and";
2
+ export * from "./or";
3
+ export * from "./not";
4
+ export * from "./filter";
@@ -0,0 +1,15 @@
1
+ import { LogicFunction } from "src/types/functions";
2
+
3
+ /**
4
+ * Groups a number of "logic functions" together by combining their results using
5
+ * the logical **NOT** operator.
6
+ *
7
+ * **Note:** a "logic function" is any function which returns a boolean
8
+ */
9
+ export const not = <T extends any[]>(op: LogicFunction<T>): LogicFunction<T> => {
10
+ const fn: LogicFunction<T> = (...args: T) => {
11
+ return !op(...args);
12
+ };
13
+
14
+ return fn;
15
+ };
@@ -0,0 +1,15 @@
1
+ import { LogicFunction } from "src/types/functions";
2
+
3
+ /**
4
+ * Groups a number of "logic functions" together by combining their results using
5
+ * the logical **OR** operator.
6
+ *
7
+ * **Note:** a "logic function" is any function which returns a boolean
8
+ */
9
+ export const or = <T extends any[]>(...ops: LogicFunction<T>[]): LogicFunction<T> => {
10
+ const fn: LogicFunction<T> = (...args: T) => {
11
+ return [...ops].some((i) => i(...args));
12
+ };
13
+
14
+ return fn;
15
+ };