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.
- package/.vscode/settings.json +1 -0
- package/dist/index.d.ts +308 -84
- package/dist/index.js +378 -262
- package/dist/index.mjs +370 -259
- package/package.json +1 -1
- package/src/types/alphabetic/Cardinality.ts +80 -0
- package/src/types/alphabetic/index.ts +1 -0
- package/src/types/dictionary/MapTo.ts +128 -15
- package/src/types/functions/LogicFunction.ts +4 -0
- package/src/types/functions/index.ts +1 -0
- package/src/types/index.ts +1 -0
- package/src/types/literal-unions/OptRequired.ts +4 -0
- package/src/types/literal-unions/index.ts +1 -0
- package/src/types/string-literals/Replace.ts +8 -4
- package/src/utility/boolean-logic/and.ts +15 -0
- package/src/utility/boolean-logic/filter.ts +268 -0
- package/src/utility/boolean-logic/index.ts +4 -0
- package/src/utility/boolean-logic/not.ts +15 -0
- package/src/utility/boolean-logic/or.ts +15 -0
- package/src/utility/dictionary/mapTo.ts +117 -30
- package/src/utility/index.ts +1 -1
- package/src/utility/lists/asArray.ts +34 -0
- package/src/utility/lists/index.ts +1 -0
- package/tests/boolean-logic/boolean.spec.ts +21 -0
- package/tests/boolean-logic/filter.spec.ts +52 -0
- package/tests/createFnWithProps.spec.ts +1 -0
- package/tests/dictionary/mapTo.test.ts +159 -92
- package/tests/lists/asArray.test.ts +91 -0
- package/src/utility/map-reduce/filter.ts +0 -35
- package/src/utility/map-reduce/index.ts +0 -12
|
@@ -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
|
-
*
|
|
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
|
|
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
|
-
* **
|
|
51
|
+
* **MapTo<I, O>**
|
|
14
52
|
*
|
|
15
|
-
*
|
|
53
|
+
* A mapping function between an input type `I` and output type `O`.
|
|
16
54
|
*
|
|
17
|
-
* **Note:**
|
|
18
|
-
*
|
|
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
|
|
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>;
|
package/src/types/index.ts
CHANGED
|
@@ -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 @@
|
|
|
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
|
-
|
|
13
|
-
|
|
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,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
|
+
};
|