inferred-types 0.26.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 +175 -57
- package/dist/index.js +41 -7
- package/dist/index.mjs +38 -7
- 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/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/dictionary/mapTo.ts +117 -30
- package/tests/createFnWithProps.spec.ts +1 -0
- package/tests/dictionary/mapTo.test.ts +159 -92
package/.vscode/settings.json
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -579,26 +579,52 @@ O extends (...args: any[]) => any = (...args: any[]) => any> = SimplifyObject<{
|
|
|
579
579
|
declare type Get<T, K> = K extends `${infer FK}.${infer L}` ? FK extends keyof T ? Get<T[FK], L> : never : K extends keyof T ? T[K] : never;
|
|
580
580
|
|
|
581
581
|
/**
|
|
582
|
-
*
|
|
582
|
+
* Expresses whether an option is "opt" (optional) or "req" (required)
|
|
583
|
+
*/
|
|
584
|
+
declare type OptRequired = "opt" | "req";
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Expresses relationship between inputs/outputs:
|
|
588
|
+
*/
|
|
589
|
+
declare enum MapDirection {
|
|
590
|
+
/** every input results in 0:M outputs */
|
|
591
|
+
OneToMany = "I -> O[]",
|
|
592
|
+
/** every input results in 0:1 outputs */
|
|
593
|
+
OneToOne = "I -> O",
|
|
594
|
+
/** every input is an array of type I and reduced to a single O */
|
|
595
|
+
ManyToOne = "I[] -> O"
|
|
596
|
+
}
|
|
597
|
+
declare type MapDirectionVal = EnumValues<MapDirection>;
|
|
598
|
+
declare type MapInput<I, IR, D extends MapDirectionVal> = D extends MapDirection.OneToMany | "I -> O[]" ? IR extends "opt" ? I | undefined : I : D extends MapDirection.OneToOne | "I -> O" ? IR extends "opt" ? I | undefined : I : D extends MapDirection.ManyToOne | "I[] -> O" ? IR extends "opt" ? I[] | undefined : I[] : never;
|
|
599
|
+
declare type MapOutput<O, OR, D extends MapDirectionVal> = D extends MapDirection.OneToMany | "I -> O[]" ? OR extends "opt" ? O[] : [O, ...O[]] : D extends MapDirection.OneToOne | "I -> O" ? OR extends "opt" ? O | null : O : D extends MapDirection.ManyToOne | "I[] -> O" ? OR extends "opt" ? O | null : O : never;
|
|
600
|
+
/**
|
|
601
|
+
* **MapTo<I, O>**
|
|
583
602
|
*
|
|
584
|
-
*
|
|
603
|
+
* A mapping function between an input type `I` and output type `O`.
|
|
585
604
|
*
|
|
586
|
-
* **Note:**
|
|
587
|
-
*
|
|
588
|
-
* instead.
|
|
605
|
+
* **Note:** this type is designed to guide the userland mapping; refer
|
|
606
|
+
* to `MapFn` if you want the type output by the `mapFn()` utility.
|
|
589
607
|
*/
|
|
590
|
-
declare type MapTo<I extends
|
|
608
|
+
declare type MapTo<I, O, IR extends OptRequired = "req", D extends MapDirectionVal = MapDirection.OneToMany, OR extends OptRequired = "opt"> = IR extends "opt" ? (source?: MapInput<I, IR, D>) => MapOutput<O, OR, D> : (source: MapInput<I, IR, D>) => MapOutput<O, OR, D>;
|
|
609
|
+
declare type MapFnOutput<I, O, S, OR extends OptRequired, D extends MapDirectionVal> = D extends "I -> O[]" | MapDirection.OneToMany ? S extends I[] ? OR extends "opt" ? O[] : [O, ...O[]] : OR extends "opt" ? O[] | null : O[] : D extends MapDirection.OneToOne | "I -> O" ? S extends I[] ? OR extends "opt" ? O[] : [O, ...O[]] : OR extends "opt" ? O | null : O : D extends MapDirection.ManyToOne | "I[] -> O" ? S extends I[][] ? OR extends "opt" ? O[] : [O, ...O[]] : OR extends "opt" ? O | null : O : never;
|
|
610
|
+
declare type MapFnInput<I, IR extends OptRequired, D extends MapDirectionVal> = D extends "I -> O[]" | MapDirection.OneToMany ? IR extends "opt" ? I | I[] | undefined : I | I[] : D extends MapDirection.OneToOne | "I -> O" ? IR extends "opt" ? I | I[] | undefined : I | I[] : D extends MapDirection.ManyToOne | "I[] -> O" ? IR extends "opt" ? I[] | I[][] | undefined : I[] | I[][] : never;
|
|
591
611
|
/**
|
|
592
|
-
*
|
|
612
|
+
* The mapping function provided by the `mapFn()` utility. This _fn_
|
|
613
|
+
* is intended to be used in two ways:
|
|
593
614
|
*
|
|
594
|
-
*
|
|
595
|
-
*
|
|
596
|
-
*
|
|
597
|
-
*
|
|
598
|
-
* out
|
|
599
|
-
*
|
|
615
|
+
* 1. Iterative:
|
|
616
|
+
* ```ts
|
|
617
|
+
* const m = mapTo<I,O>(i => [ ... ]);
|
|
618
|
+
* // maps inputs to outputs
|
|
619
|
+
* const out = inputs.map(m);
|
|
620
|
+
* ```
|
|
621
|
+
* 2. Block:
|
|
622
|
+
* ```ts
|
|
623
|
+
* // maps inputs to outputs (filtering or splitting where approp)
|
|
624
|
+
* const out2 = m(inputs);
|
|
625
|
+
* ```
|
|
600
626
|
*/
|
|
601
|
-
declare type
|
|
627
|
+
declare type MapFn<I, O, IR extends OptRequired = "req", D extends MapDirectionVal = MapDirection.OneToMany, OR extends OptRequired = "opt"> = IR extends "opt" ? <S extends MapFnInput<I, IR, D>>(source?: S) => MapFnOutput<I, O, S, OR, D> : <S extends MapFnInput<I, IR, D>>(source: S) => MapFnOutput<I, O, S, OR, D>;
|
|
602
628
|
|
|
603
629
|
/**
|
|
604
630
|
* Given a dictionary of type `<T>`, this utility function will
|
|
@@ -1061,6 +1087,77 @@ declare type _DU<T extends string> = T extends Lowercase<T> ? T : `-${Lowercase<
|
|
|
1061
1087
|
*/
|
|
1062
1088
|
declare type DashUppercase<T extends string> = HasUppercase<T> extends false ? T : T extends `${infer C0}${infer C1}${infer R}` ? `${_DU<C0>}${_DU<C1>}${DashUppercase<R>}` : T extends `${infer C0}${infer R}` ? `${_DU<C0>}${DashUppercase<R>}` : "";
|
|
1063
1089
|
|
|
1090
|
+
/**
|
|
1091
|
+
* Converts a Tuple type into a _union_ of the tuple elements
|
|
1092
|
+
* ```ts
|
|
1093
|
+
* const arr = [1, 3, 5] as const;
|
|
1094
|
+
* // 1 | 3 | 5
|
|
1095
|
+
* type U = TupleToUnion<typeof arr>;
|
|
1096
|
+
* ```
|
|
1097
|
+
*/
|
|
1098
|
+
declare type TupleToUnion<T> = Mutable<T> extends any[] ? Mutable<T>[number] : never;
|
|
1099
|
+
|
|
1100
|
+
/**
|
|
1101
|
+
* LastInUnion<1 | 2> = 2.
|
|
1102
|
+
*/
|
|
1103
|
+
declare type LastInUnion<U> = UnionToIntersection<U extends unknown ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L : never;
|
|
1104
|
+
/**
|
|
1105
|
+
* UnionToTuple<1 | 2> = [1, 2].
|
|
1106
|
+
*/
|
|
1107
|
+
declare type UnionToTuple<U, Last = LastInUnion<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, Last>>, Last];
|
|
1108
|
+
|
|
1109
|
+
declare type Widen<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
|
|
1110
|
+
|
|
1111
|
+
declare type OneToOne = `1:1`;
|
|
1112
|
+
declare type OneToMany = `1:M`;
|
|
1113
|
+
declare type OneToZero = `1:0`;
|
|
1114
|
+
declare type ZeroToOne = `0:1`;
|
|
1115
|
+
declare type ZeroToMany = `0:M`;
|
|
1116
|
+
declare type ZeroToZero = `0:0`;
|
|
1117
|
+
declare type ManyToMany = "M:M";
|
|
1118
|
+
declare type ManyToOne = "M:1";
|
|
1119
|
+
declare type ManyToZero = "M:0";
|
|
1120
|
+
declare type CardinalityNode = "0" | "1" | "M";
|
|
1121
|
+
/**
|
|
1122
|
+
* Cardinality which expects a singular input and requires
|
|
1123
|
+
* 1 or many outputs.
|
|
1124
|
+
*
|
|
1125
|
+
* Note: choose `CardinalityFilter1` if you want to allow output
|
|
1126
|
+
* to have no outputs.
|
|
1127
|
+
*/
|
|
1128
|
+
declare type Cardinality1 = OneToOne | OneToMany;
|
|
1129
|
+
/**
|
|
1130
|
+
* Cardinality which expects a singular input and maps to 0,
|
|
1131
|
+
* 1, or many outputs.
|
|
1132
|
+
*/
|
|
1133
|
+
declare type CardinalityFilter1 = OneToOne | OneToMany | OneToZero;
|
|
1134
|
+
/**
|
|
1135
|
+
* Cardinality which expects a singular input which is allowed to be
|
|
1136
|
+
* _undefined_ or the expected type.
|
|
1137
|
+
*/
|
|
1138
|
+
declare type Cardinality0 = ZeroToOne | ZeroToMany | OneToOne | OneToMany;
|
|
1139
|
+
/**
|
|
1140
|
+
* Cardinality which expects a singular input -- which is allowed to be
|
|
1141
|
+
* _undefined_ -- and maps to 0,
|
|
1142
|
+
* 1, or many outputs.
|
|
1143
|
+
*/
|
|
1144
|
+
declare type CardinalityFilter0 = ZeroToOne | ZeroToMany | OneToOne | OneToMany | OneToZero | ZeroToZero;
|
|
1145
|
+
declare type CardinalityExplicit = `${number}:${number}`;
|
|
1146
|
+
/**
|
|
1147
|
+
* Cardinality of any sort between two types
|
|
1148
|
+
*/
|
|
1149
|
+
declare type Cardinality = CardinalityFilter0 | CardinalityFilter1 | ManyToMany | ManyToOne | ManyToZero | CardinalityExplicit;
|
|
1150
|
+
declare type CardinalityTuple<T extends Cardinality> = UnionToTuple<T>;
|
|
1151
|
+
/**
|
|
1152
|
+
* The first or _input_ part of the Cardinality relationship
|
|
1153
|
+
*/
|
|
1154
|
+
declare type CardinalityIn<T extends Cardinality> = T extends `${infer IN}:${string}` ? IN : never;
|
|
1155
|
+
/**
|
|
1156
|
+
* The second or _output_ part of the Cardinality relationship
|
|
1157
|
+
*/
|
|
1158
|
+
declare type CardinalityOut<T extends Cardinality> = T extends `${string}:${infer OUT}` ? OUT : never;
|
|
1159
|
+
declare type CardinalityInput<T, C extends Cardinality> = CardinalityTuple<C>[0] extends 0 ? T | undefined : CardinalityTuple<C>[0] extends 1 ? T : T[];
|
|
1160
|
+
|
|
1064
1161
|
/**
|
|
1065
1162
|
* Converts a string literal into a _dasherized_ format while ignoring _exterior_ whitespace.
|
|
1066
1163
|
*
|
|
@@ -1277,15 +1374,6 @@ declare type DictArray<T> = Array<{
|
|
|
1277
1374
|
[K in keyof T]: DictArrayKv<K, T>;
|
|
1278
1375
|
}[keyof T]>;
|
|
1279
1376
|
|
|
1280
|
-
/**
|
|
1281
|
-
* LastInUnion<1 | 2> = 2.
|
|
1282
|
-
*/
|
|
1283
|
-
declare type LastInUnion<U> = UnionToIntersection<U extends unknown ? (x: U) => 0 : never> extends (x: infer L) => 0 ? L : never;
|
|
1284
|
-
/**
|
|
1285
|
-
* UnionToTuple<1 | 2> = [1, 2].
|
|
1286
|
-
*/
|
|
1287
|
-
declare type UnionToTuple<U, Last = LastInUnion<U>> = [U] extends [never] ? [] : [...UnionToTuple<Exclude<U, Last>>, Last];
|
|
1288
|
-
|
|
1289
1377
|
/**
|
|
1290
1378
|
* Returns the _first_ key in an object.
|
|
1291
1379
|
*
|
|
@@ -1314,18 +1402,6 @@ declare type FirstKeyValue<T extends object> = FirstKey<T> extends keyof T ? T[F
|
|
|
1314
1402
|
*/
|
|
1315
1403
|
declare type FirstOfEach<T extends readonly any[][]> = T[number][0] extends T[number][number] ? T[number][0] : never;
|
|
1316
1404
|
|
|
1317
|
-
/**
|
|
1318
|
-
* Converts a Tuple type into a _union_ of the tuple elements
|
|
1319
|
-
* ```ts
|
|
1320
|
-
* const arr = [1, 3, 5] as const;
|
|
1321
|
-
* // 1 | 3 | 5
|
|
1322
|
-
* type U = TupleToUnion<typeof arr>;
|
|
1323
|
-
* ```
|
|
1324
|
-
*/
|
|
1325
|
-
declare type TupleToUnion<T> = Mutable<T> extends any[] ? Mutable<T>[number] : never;
|
|
1326
|
-
|
|
1327
|
-
declare type Widen<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
|
|
1328
|
-
|
|
1329
1405
|
/**
|
|
1330
1406
|
* Typescript utility which receives `T` as shape which resembles `DictArray<D>`
|
|
1331
1407
|
* and if the type `D` can be inferred it is returned.
|
|
@@ -1624,32 +1700,74 @@ declare function entries<N extends Narrowable, T extends Record<string, N>, I ex
|
|
|
1624
1700
|
*/
|
|
1625
1701
|
declare function mapValues<N extends Narrowable, T extends Record<string, N>, V>(obj: T, valueMapper: (k: T[keyof T]) => V): { [K in keyof T]: V; };
|
|
1626
1702
|
|
|
1703
|
+
interface MapConfig<IR extends OptRequired = "req", D extends MapDirectionVal = "I -> O[]", OR extends OptRequired = "opt"> {
|
|
1704
|
+
input?: IR;
|
|
1705
|
+
output?: OR;
|
|
1706
|
+
direction?: D;
|
|
1707
|
+
}
|
|
1627
1708
|
/**
|
|
1628
|
-
*
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
*
|
|
1633
|
-
*
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
*
|
|
1638
|
-
* ```ts
|
|
1639
|
-
* const mapper = mapTo<I,O>(i => i.name
|
|
1640
|
-
* ? [
|
|
1641
|
-
* { name: i.name, a: "static text", b: i.products.length }
|
|
1642
|
-
* ]
|
|
1643
|
-
* : null
|
|
1644
|
-
* );
|
|
1645
|
-
* ```
|
|
1709
|
+
* Provides a mapper function using the default configuration
|
|
1710
|
+
*/
|
|
1711
|
+
declare type MapperApiDefault = <I, O>(map: MapTo<I, O>) => MapFn<I, O>;
|
|
1712
|
+
/**
|
|
1713
|
+
* Provides opportunity to configure _input_ and _output_ relationships
|
|
1714
|
+
* prior to providing a mapping function.
|
|
1715
|
+
*/
|
|
1716
|
+
declare type MapperApiConfigured = <IR extends OptRequired = "req", D extends MapDirectionVal = "I -> O[]", OR extends OptRequired = "opt">(config: MapConfig<IR, D, OR>) => <I, O>(map: MapTo<I, O, IR, D, OR>) => MapFn<I, O, IR, D, OR>;
|
|
1717
|
+
/**
|
|
1718
|
+
* **mapTo** _utility_
|
|
1646
1719
|
*
|
|
1647
|
-
*
|
|
1720
|
+
* This utility creates a strongly typed data mapper which maps from one
|
|
1721
|
+
* known source `I` to another `O`:
|
|
1648
1722
|
* ```ts
|
|
1649
|
-
*
|
|
1723
|
+
* const mapper = mapTo<I, O>(i => [{
|
|
1724
|
+
* foo: i.bar
|
|
1725
|
+
* }]);
|
|
1650
1726
|
* ```
|
|
1651
1727
|
*/
|
|
1652
|
-
declare const
|
|
1728
|
+
declare const mapToFn: <I, O>(map: (source: I) => O[]) => <S extends I | I[]>(source: S) => S extends I[] ? O[] : O[] | null;
|
|
1729
|
+
/**
|
|
1730
|
+
* Provides a `config` method which allows the relationships between _inputs_
|
|
1731
|
+
* and _outputs_ to be configured.
|
|
1732
|
+
*/
|
|
1733
|
+
declare const mapToDict: {
|
|
1734
|
+
/** configure the relationship between the _inputs_ and _outputs_ */
|
|
1735
|
+
config: <IR extends OptRequired = "req", D extends "I -> O[]" | "I -> O" | "I[] -> O" = "I -> O[]", OR extends OptRequired = "opt">(c?: MapConfig<IR, D, OR>) => {
|
|
1736
|
+
/**
|
|
1737
|
+
* create your mapping with your recently setup configuration:
|
|
1738
|
+
* ```ts
|
|
1739
|
+
* const mapper = mapTo
|
|
1740
|
+
* .config({ ... })
|
|
1741
|
+
* .map<I, O>(i => [{
|
|
1742
|
+
* foo: i.bar
|
|
1743
|
+
* }]);
|
|
1744
|
+
* ```
|
|
1745
|
+
*/
|
|
1746
|
+
map<I, O>(map: MapTo<I, O, IR, D, OR>): MapFn<I, O, IR, D, OR>;
|
|
1747
|
+
};
|
|
1748
|
+
};
|
|
1749
|
+
/**
|
|
1750
|
+
* **mapTo** _utility_
|
|
1751
|
+
*
|
|
1752
|
+
* This utility creates a strongly typed data mapper which maps from one
|
|
1753
|
+
* known source `I` to another `O`.
|
|
1754
|
+
*/
|
|
1755
|
+
declare const mapTo: (<I, O>(map: (source: I) => O[]) => <S extends I | I[]>(source: S) => S extends I[] ? O[] : O[] | null) & {
|
|
1756
|
+
/** configure the relationship between the _inputs_ and _outputs_ */
|
|
1757
|
+
config: <IR extends OptRequired = "req", D extends "I -> O[]" | "I -> O" | "I[] -> O" = "I -> O[]", OR extends OptRequired = "opt">(c?: MapConfig<IR, D, OR>) => {
|
|
1758
|
+
/**
|
|
1759
|
+
* create your mapping with your recently setup configuration:
|
|
1760
|
+
* ```ts
|
|
1761
|
+
* const mapper = mapTo
|
|
1762
|
+
* .config({ ... })
|
|
1763
|
+
* .map<I, O>(i => [{
|
|
1764
|
+
* foo: i.bar
|
|
1765
|
+
* }]);
|
|
1766
|
+
* ```
|
|
1767
|
+
*/
|
|
1768
|
+
map<I, O>(map: MapTo<I, O, IR, D, OR>): MapFn<I, O, IR, D, OR>;
|
|
1769
|
+
};
|
|
1770
|
+
};
|
|
1653
1771
|
|
|
1654
1772
|
/**
|
|
1655
1773
|
* converts an array of strings `["a", "b", "c"]` into a more type friendly
|
|
@@ -1927,4 +2045,4 @@ interface IFluentConfigurator<C> {
|
|
|
1927
2045
|
*/
|
|
1928
2046
|
declare function FluentConfigurator<I>(initial?: I): IFluentConfigurator<{}>;
|
|
1929
2047
|
|
|
1930
|
-
export { AllCaps, Alpha, AlphaNumeric, Api, ApiFunction, ApiValue, AppendToDictionary, AppendToObject, ArrConcat, Array$1 as Array, ArrayConverter, AsArray, AssertExtends, Bracket, Break, CamelCase, CapitalizeWords, ClosingBracket, Condition, ConditionFilter, Configurator, Constructor, DashToSnake, DashUppercase, Dasherize, DefinePropertiesApi, DictArrApi, DictArray, DictArrayFilterCallback, DictArrayKv, DictChangeValue, DictFromKv, DictKvTuple, DictPartialApplication, DictPrependWithFn, DictReturnValues, DynamicRule, DynamicRuleSet, Email, EnumValues, ExpandRecursively, ExpectExtends, ExplicitFunction, ExtendsClause, ExtendsNarrowlyClause, FilterContains, FilterDefn, FilterEnds, FilterEquals, FilterFn, FilterGreaterThan, FilterIs, FilterLessThan, FilterNotEqual, FilterStarts, FinalReturn, First, FirstKey, FirstKeyValue, FirstOfEach, FluentConfigurator, FluentFunction, FromDictArray, FunctionType, GeneralDictionary, Get, HasUppercase, IConfigurator, IFluentConfigurator, If, IfExtends, IfExtendsThen, Include, Includes, IsArray, IsBoolean, IsBooleanLiteral, IsCapitalized, IsFalse, IsFunction, IsLiteral, IsNumericLiteral, IsObject, IsString, IsStringLiteral, IsTrue, KebabCase, KeyValue, KeyedRecord, Keys, KeysWithValue, KvFrom, KvTuple, LastInUnion, LeftWhitespace, Length, LogicFunction, LogicalCombinator, LowerAllCaps, LowerAlpha, MapTo,
|
|
2048
|
+
export { AllCaps, Alpha, AlphaNumeric, Api, ApiFunction, ApiValue, AppendToDictionary, AppendToObject, ArrConcat, Array$1 as Array, ArrayConverter, AsArray, AssertExtends, Bracket, Break, CamelCase, CapitalizeWords, Cardinality, Cardinality0, Cardinality1, CardinalityExplicit, CardinalityFilter0, CardinalityFilter1, CardinalityIn, CardinalityInput, CardinalityNode, CardinalityOut, CardinalityTuple, ClosingBracket, Condition, ConditionFilter, Configurator, Constructor, DashToSnake, DashUppercase, Dasherize, DefinePropertiesApi, DictArrApi, DictArray, DictArrayFilterCallback, DictArrayKv, DictChangeValue, DictFromKv, DictKvTuple, DictPartialApplication, DictPrependWithFn, DictReturnValues, DynamicRule, DynamicRuleSet, Email, EnumValues, ExpandRecursively, ExpectExtends, ExplicitFunction, ExtendsClause, ExtendsNarrowlyClause, FilterContains, FilterDefn, FilterEnds, FilterEquals, FilterFn, FilterGreaterThan, FilterIs, FilterLessThan, FilterNotEqual, FilterStarts, FinalReturn, First, FirstKey, FirstKeyValue, FirstOfEach, FluentConfigurator, FluentFunction, FromDictArray, FunctionType, GeneralDictionary, Get, HasUppercase, IConfigurator, IFluentConfigurator, If, IfExtends, IfExtendsThen, Include, Includes, IsArray, IsBoolean, IsBooleanLiteral, IsCapitalized, IsFalse, IsFunction, IsLiteral, IsNumericLiteral, IsObject, IsString, IsStringLiteral, IsTrue, KebabCase, KeyValue, KeyedRecord, Keys, KeysWithValue, KvFrom, KvTuple, LastInUnion, LeftWhitespace, Length, LogicFunction, LogicalCombinator, LowerAllCaps, LowerAlpha, ManyToMany, ManyToOne, ManyToZero, MapConfig, MapDirection, MapDirectionVal, MapFn, MapFnInput, MapFnOutput, MapInput, MapOutput, MapTo, MapperApiConfigured, MapperApiDefault, MaybeFalse, MaybeTrue, Model, Mutable, MutableProps, MutationFunction, MutationIdentity, Narrowable, NonAlpha, NonNumericKeys, NonStringKeys, Not, NotFilter, Numeric, NumericFilter, NumericKeys, NumericString, ObjectType, OneToMany, OneToOne, OneToZero, Opaque, OpeningBracket, OptRequired, OptionalKeys, OptionalProps, Parenthesis, PascalCase, Pluralize, PrivateKey, PrivateKeys, PublicKeys, Punctuation, PureFluentApi, Replace, RequireProps, RequiredKeys, RequiredProps, Retain, RightWhitespace, RuleDefinition, RuntimeProp, RuntimeType, SameKeys, SecondOfEach, SimplifyObject, SnakeCase, SpecialCharacters, StringDelimiter, StringFilter, StringKeys, StringLength, ToFluent, Transformer, Trim, TrimLeft, TrimRight, TupleToUnion, Type, TypeApi, TypeCondition, TypeDefinition, TypeGuard, TypeOptions, UndefinedArrayIsUnknown, UndefinedTreatment, UndefinedValue, UnionToIntersection, UnionToTuple, UniqueDictionary, UniqueForProp, Uniqueness, UnwrapNot, UpperAlpha, ValueTuple, ValueTypeFunc, ValueTypes, Where, WhereNot, Whitespace, Widen, WithNumericKeys, WithStringKeys, WithValue, ZeroToMany, ZeroToOne, ZeroToZero, ZipCode, and, api, arrayToKeyLookup, arrayToObject, asArray, condition, createFnWithProps, createMutationFunction, defineProperties, defineType, dictArr, dictToKv, dictionaryTransform, entries, filter, filterDictArray, fnWithProps, groupBy, idLiteral, idTypeGuard, identity, ifTypeOf, isArray, isBoolean, isFalse, isFunction, isNotFilter, isNull, isNumber, isNumericFilter, isObject, isString, isSymbol, isTrue, isType, isUndefined, keys, kindLiteral, kv, kvToDict, literal, mapTo, mapToDict, mapToFn, mapValues, nameLiteral, not, or, randomString, readonlyFnWithProps, ruleSet, strArrayToDict, type, typeApi, uuid, valueTypes, withValue };
|
package/dist/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __export(src_exports, {
|
|
|
23
23
|
Configurator: () => Configurator,
|
|
24
24
|
ExplicitFunction: () => ExplicitFunction,
|
|
25
25
|
FluentConfigurator: () => FluentConfigurator,
|
|
26
|
+
MapDirection: () => MapDirection,
|
|
26
27
|
Model: () => Model,
|
|
27
28
|
MutationIdentity: () => MutationIdentity,
|
|
28
29
|
and: () => and,
|
|
@@ -67,6 +68,8 @@ __export(src_exports, {
|
|
|
67
68
|
kvToDict: () => kvToDict,
|
|
68
69
|
literal: () => literal,
|
|
69
70
|
mapTo: () => mapTo,
|
|
71
|
+
mapToDict: () => mapToDict,
|
|
72
|
+
mapToFn: () => mapToFn,
|
|
70
73
|
mapValues: () => mapValues,
|
|
71
74
|
nameLiteral: () => nameLiteral,
|
|
72
75
|
not: () => not,
|
|
@@ -126,6 +129,14 @@ var valueTypes = {
|
|
|
126
129
|
literalArray: (arr) => [arr, true]
|
|
127
130
|
};
|
|
128
131
|
|
|
132
|
+
// src/types/dictionary/MapTo.ts
|
|
133
|
+
var MapDirection = /* @__PURE__ */ ((MapDirection2) => {
|
|
134
|
+
MapDirection2["OneToMany"] = "I -> O[]";
|
|
135
|
+
MapDirection2["OneToOne"] = "I -> O";
|
|
136
|
+
MapDirection2["ManyToOne"] = "I[] -> O";
|
|
137
|
+
return MapDirection2;
|
|
138
|
+
})(MapDirection || {});
|
|
139
|
+
|
|
129
140
|
// src/utility/keys.ts
|
|
130
141
|
function keys(obj, ...without) {
|
|
131
142
|
const v = without.length > 0 ? Object.keys(obj).filter((k) => !without.includes(k)) : Object.keys(obj);
|
|
@@ -579,14 +590,34 @@ function mapValues(obj, valueMapper) {
|
|
|
579
590
|
}
|
|
580
591
|
|
|
581
592
|
// src/utility/dictionary/mapTo.ts
|
|
582
|
-
var
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
}
|
|
593
|
+
var mapper = (config = {}) => (map) => {
|
|
594
|
+
config = {
|
|
595
|
+
input: "req",
|
|
596
|
+
output: "opt",
|
|
597
|
+
direction: "I -> O[]",
|
|
598
|
+
...config
|
|
599
|
+
};
|
|
600
|
+
const fn = (source) => {
|
|
601
|
+
const isArray2 = config.direction === "I -> O[]" && Array.isArray(source) ? true : config.direction === "I[] -> O" && Array.isArray(source) && Array.isArray(source[0]) ? true : false;
|
|
602
|
+
if (isArray2) {
|
|
603
|
+
return source.flatMap(map);
|
|
604
|
+
} else {
|
|
605
|
+
return map(source);
|
|
606
|
+
}
|
|
607
|
+
};
|
|
608
|
+
return fn;
|
|
609
|
+
};
|
|
610
|
+
var mapToFn = (map) => {
|
|
611
|
+
return mapper()(map);
|
|
612
|
+
};
|
|
613
|
+
var mapToDict = {
|
|
614
|
+
config: (c = { input: "req", output: "opt", direction: "I -> O[]" }) => ({
|
|
615
|
+
map(map) {
|
|
616
|
+
return mapper(c)(map);
|
|
617
|
+
}
|
|
618
|
+
})
|
|
589
619
|
};
|
|
620
|
+
var mapTo = createFnWithProps(mapToFn, mapToDict);
|
|
590
621
|
|
|
591
622
|
// src/utility/dictionary/strArrayToDict.ts
|
|
592
623
|
function strArrayToDict(...strings) {
|
|
@@ -754,6 +785,7 @@ function Model(name) {
|
|
|
754
785
|
Configurator,
|
|
755
786
|
ExplicitFunction,
|
|
756
787
|
FluentConfigurator,
|
|
788
|
+
MapDirection,
|
|
757
789
|
Model,
|
|
758
790
|
MutationIdentity,
|
|
759
791
|
and,
|
|
@@ -798,6 +830,8 @@ function Model(name) {
|
|
|
798
830
|
kvToDict,
|
|
799
831
|
literal,
|
|
800
832
|
mapTo,
|
|
833
|
+
mapToDict,
|
|
834
|
+
mapToFn,
|
|
801
835
|
mapValues,
|
|
802
836
|
nameLiteral,
|
|
803
837
|
not,
|
package/dist/index.mjs
CHANGED
|
@@ -41,6 +41,14 @@ var valueTypes = {
|
|
|
41
41
|
literalArray: (arr) => [arr, true]
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
// src/types/dictionary/MapTo.ts
|
|
45
|
+
var MapDirection = /* @__PURE__ */ ((MapDirection2) => {
|
|
46
|
+
MapDirection2["OneToMany"] = "I -> O[]";
|
|
47
|
+
MapDirection2["OneToOne"] = "I -> O";
|
|
48
|
+
MapDirection2["ManyToOne"] = "I[] -> O";
|
|
49
|
+
return MapDirection2;
|
|
50
|
+
})(MapDirection || {});
|
|
51
|
+
|
|
44
52
|
// src/utility/keys.ts
|
|
45
53
|
function keys(obj, ...without) {
|
|
46
54
|
const v = without.length > 0 ? Object.keys(obj).filter((k) => !without.includes(k)) : Object.keys(obj);
|
|
@@ -494,14 +502,34 @@ function mapValues(obj, valueMapper) {
|
|
|
494
502
|
}
|
|
495
503
|
|
|
496
504
|
// src/utility/dictionary/mapTo.ts
|
|
497
|
-
var
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
}
|
|
505
|
+
var mapper = (config = {}) => (map) => {
|
|
506
|
+
config = {
|
|
507
|
+
input: "req",
|
|
508
|
+
output: "opt",
|
|
509
|
+
direction: "I -> O[]",
|
|
510
|
+
...config
|
|
511
|
+
};
|
|
512
|
+
const fn = (source) => {
|
|
513
|
+
const isArray2 = config.direction === "I -> O[]" && Array.isArray(source) ? true : config.direction === "I[] -> O" && Array.isArray(source) && Array.isArray(source[0]) ? true : false;
|
|
514
|
+
if (isArray2) {
|
|
515
|
+
return source.flatMap(map);
|
|
516
|
+
} else {
|
|
517
|
+
return map(source);
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
return fn;
|
|
521
|
+
};
|
|
522
|
+
var mapToFn = (map) => {
|
|
523
|
+
return mapper()(map);
|
|
524
|
+
};
|
|
525
|
+
var mapToDict = {
|
|
526
|
+
config: (c = { input: "req", output: "opt", direction: "I -> O[]" }) => ({
|
|
527
|
+
map(map) {
|
|
528
|
+
return mapper(c)(map);
|
|
529
|
+
}
|
|
530
|
+
})
|
|
504
531
|
};
|
|
532
|
+
var mapTo = createFnWithProps(mapToFn, mapToDict);
|
|
505
533
|
|
|
506
534
|
// src/utility/dictionary/strArrayToDict.ts
|
|
507
535
|
function strArrayToDict(...strings) {
|
|
@@ -668,6 +696,7 @@ export {
|
|
|
668
696
|
Configurator,
|
|
669
697
|
ExplicitFunction,
|
|
670
698
|
FluentConfigurator,
|
|
699
|
+
MapDirection,
|
|
671
700
|
Model,
|
|
672
701
|
MutationIdentity,
|
|
673
702
|
and,
|
|
@@ -712,6 +741,8 @@ export {
|
|
|
712
741
|
kvToDict,
|
|
713
742
|
literal,
|
|
714
743
|
mapTo,
|
|
744
|
+
mapToDict,
|
|
745
|
+
mapToFn,
|
|
715
746
|
mapValues,
|
|
716
747
|
nameLiteral,
|
|
717
748
|
not,
|
package/package.json
CHANGED
|
@@ -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;
|
|
@@ -1,37 +1,124 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { MapDirectionVal, MapFn, MapFnInput, MapTo } from "src/types/dictionary";
|
|
2
|
+
import { OptRequired } from "src/types/literal-unions";
|
|
3
|
+
import { createFnWithProps } from "../createFnWithProps";
|
|
4
|
+
|
|
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
|
+
}
|
|
2
14
|
|
|
3
15
|
/**
|
|
4
|
-
*
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
|
|
16
|
+
* Provides a mapper function using the default configuration
|
|
17
|
+
*/
|
|
18
|
+
export type MapperApiDefault = <I, O>(map: MapTo<I, O>) => MapFn<I, O>;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Provides opportunity to configure _input_ and _output_ relationships
|
|
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"
|
|
28
|
+
>(
|
|
29
|
+
config: MapConfig<IR, D, OR>
|
|
30
|
+
) => <I, O>(map: MapTo<I, O, IR, D, OR>) => MapFn<I, O, IR, D, OR>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The single implementation for all mapping
|
|
34
|
+
*/
|
|
35
|
+
const mapper =
|
|
36
|
+
<
|
|
37
|
+
IR extends OptRequired = "req",
|
|
38
|
+
D extends MapDirectionVal = "I -> O[]",
|
|
39
|
+
OR extends OptRequired = "opt"
|
|
40
|
+
>(
|
|
41
|
+
config: MapConfig<IR, D, OR> = {}
|
|
42
|
+
) =>
|
|
43
|
+
<I, O>(map: MapTo<I, O, IR, D, OR>) => {
|
|
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) => {
|
|
53
|
+
const isArray =
|
|
54
|
+
config.direction === "I -> O[]" && Array.isArray(source)
|
|
55
|
+
? true
|
|
56
|
+
: config.direction === "I[] -> O" && Array.isArray(source) && Array.isArray(source[0])
|
|
57
|
+
? true
|
|
58
|
+
: false;
|
|
59
|
+
|
|
60
|
+
if (isArray) {
|
|
61
|
+
return (source as any).flatMap(map);
|
|
62
|
+
} else {
|
|
63
|
+
return map(source as any);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return fn as unknown as MapFn<I, O, IR, D, OR>;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* **mapTo** _utility_
|
|
12
72
|
*
|
|
13
|
-
* This
|
|
73
|
+
* This utility creates a strongly typed data mapper which maps from one
|
|
74
|
+
* known source `I` to another `O`:
|
|
14
75
|
* ```ts
|
|
15
|
-
* const mapper = mapTo<I,O>(i =>
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* ]
|
|
19
|
-
* : null
|
|
20
|
-
* );
|
|
76
|
+
* const mapper = mapTo<I, O>(i => [{
|
|
77
|
+
* foo: i.bar
|
|
78
|
+
* }]);
|
|
21
79
|
* ```
|
|
80
|
+
*/
|
|
81
|
+
export const mapToFn = <I, O>(map: MapTo<I, O>) => {
|
|
82
|
+
return mapper()<I, O>(map);
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Provides a `config` method which allows the relationships between _inputs_
|
|
87
|
+
* and _outputs_ to be configured.
|
|
88
|
+
*/
|
|
89
|
+
export const mapToDict = {
|
|
90
|
+
/** configure the relationship between the _inputs_ and _outputs_ */
|
|
91
|
+
config: <
|
|
92
|
+
IR extends OptRequired = "req",
|
|
93
|
+
D extends MapDirectionVal = "I -> O[]",
|
|
94
|
+
OR extends OptRequired = "opt"
|
|
95
|
+
>(
|
|
96
|
+
c: MapConfig<IR, D, OR> = { input: "req", output: "opt", direction: "I -> O[]" } as MapConfig<
|
|
97
|
+
IR,
|
|
98
|
+
D,
|
|
99
|
+
OR
|
|
100
|
+
>
|
|
101
|
+
) => ({
|
|
102
|
+
/**
|
|
103
|
+
* create your mapping with your recently setup configuration:
|
|
104
|
+
* ```ts
|
|
105
|
+
* const mapper = mapTo
|
|
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
|
+
}),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* **mapTo** _utility_
|
|
22
120
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* function (source: I | I[]): O[]
|
|
26
|
-
* ```
|
|
121
|
+
* This utility creates a strongly typed data mapper which maps from one
|
|
122
|
+
* known source `I` to another `O`.
|
|
27
123
|
*/
|
|
28
|
-
export const mapTo =
|
|
29
|
-
<I extends {}, O extends {}>(cb: MapToWithFiltering<I, O>) =>
|
|
30
|
-
(source: I | I[]) => {
|
|
31
|
-
if (Array.isArray(source)) {
|
|
32
|
-
return source.flatMap((i) => cb(i)).filter((i) => i !== null) as O[];
|
|
33
|
-
} else {
|
|
34
|
-
const result = cb(source);
|
|
35
|
-
return result ? result : ([] as O[]);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
124
|
+
export const mapTo = createFnWithProps(mapToFn, mapToDict);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { MapTo } from "src/types/dictionary";
|
|
1
|
+
import { MapDirection, MapFn, MapTo } from "src/types/dictionary";
|
|
2
2
|
import { describe, expect, it } from "vitest";
|
|
3
3
|
|
|
4
4
|
import type { Expect, Equal } from "@type-challenges/utils";
|
|
@@ -10,115 +10,182 @@ const i2: I = { title: "i2", color: "green", products: ["foo", "bar", "baz"] };
|
|
|
10
10
|
|
|
11
11
|
type O = { title: string; count: number; unnecessary?: number };
|
|
12
12
|
|
|
13
|
-
describe("MapTo<
|
|
14
|
-
it("
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
describe("MapTo<I,O> and MapToFn<I,O>", () => {
|
|
14
|
+
it("MapTo with 0:M, 0:M cardinality", () => {
|
|
15
|
+
type Fn = MapTo<I, O>;
|
|
16
|
+
type Fn2 = MapTo<I, O, "req", MapDirection.OneToMany, "opt">;
|
|
17
|
+
type WFn = MapFn<I, O>;
|
|
18
|
+
type WFn2 = MapFn<I, O, "req", MapDirection.OneToMany, "opt">;
|
|
19
|
+
|
|
20
|
+
/** userland mapping function */
|
|
21
|
+
type T = (source: I) => O[];
|
|
22
|
+
/** exposed via mapTo() util */
|
|
23
|
+
type WT = <S extends I | I[]>(source: S) => S extends I[] ? O[] : O[] | null;
|
|
24
|
+
|
|
25
|
+
type cases = [
|
|
26
|
+
Expect<Equal<Fn, Fn2>>, // implicit default = explicit values
|
|
27
|
+
Expect<Equal<WFn, WFn2>>, // implicit default = explicit values
|
|
28
|
+
Expect<Equal<Fn, T>>,
|
|
29
|
+
Expect<Equal<WFn, WT>>
|
|
20
30
|
];
|
|
21
|
-
|
|
22
|
-
type cases = [Expect<Equal<M, MapTo<I, O>>>];
|
|
23
|
-
const cases: cases = [true];
|
|
24
|
-
expect(cases).toBe(cases);
|
|
31
|
+
const cases: cases = [true, true, true, true];
|
|
25
32
|
});
|
|
26
33
|
|
|
27
|
-
it("
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
it("MapTo<X> with I -> O[] variable config", () => {
|
|
35
|
+
type Fn0 = MapTo<I, O, "req", MapDirection.OneToMany, "opt">;
|
|
36
|
+
type Fn1 = MapTo<I, O, "req", MapDirection.OneToMany, "req">;
|
|
37
|
+
type Fn2 = MapTo<I, O, "opt", MapDirection.OneToMany, "opt">;
|
|
38
|
+
type Fn3 = MapTo<I, O, "opt", "I -> O[]", "req">;
|
|
39
|
+
|
|
40
|
+
type T0 = (source: I) => O[];
|
|
41
|
+
type T1 = (source: I) => [O, ...O[]];
|
|
42
|
+
type T2 = (source?: I) => O[];
|
|
43
|
+
type T3 = (source?: I) => [O, ...O[]];
|
|
44
|
+
|
|
45
|
+
type cases = [
|
|
46
|
+
Expect<Equal<Fn0, T0>>, //
|
|
47
|
+
Expect<Equal<Fn1, T1>>, //
|
|
48
|
+
Expect<Equal<Fn2, T2>>, //
|
|
49
|
+
Expect<Equal<Fn3, T3>> //
|
|
34
50
|
];
|
|
35
|
-
|
|
36
|
-
type cases = [Expect<Equal<M, MapTo<I, O>>>];
|
|
37
|
-
const cases: cases = [true];
|
|
38
|
-
expect(cases).toBe(cases);
|
|
51
|
+
const cases: cases = [true, true, true, true];
|
|
39
52
|
});
|
|
40
53
|
|
|
41
|
-
it("
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
54
|
+
it("MapTo<X> with I[] -> O variable config", () => {
|
|
55
|
+
type Fn0 = MapTo<I, O, "req", "I[] -> O", "opt">;
|
|
56
|
+
type Fn1 = MapTo<I, O, "req", "I[] -> O", "req">;
|
|
57
|
+
type Fn2 = MapTo<I, O, "opt", "I[] -> O", "opt">;
|
|
58
|
+
type Fn3 = MapTo<I, O, "opt", "I[] -> O", "req">;
|
|
59
|
+
|
|
60
|
+
type T0 = (source: I[]) => O | null;
|
|
61
|
+
type T1 = (source: I[]) => O;
|
|
62
|
+
type T2 = (source?: I[]) => O | null;
|
|
63
|
+
type T3 = (source?: I[]) => O;
|
|
64
|
+
|
|
65
|
+
type cases = [
|
|
66
|
+
Expect<Equal<Fn0, T0>>, //
|
|
67
|
+
Expect<Equal<Fn1, T1>>, //
|
|
68
|
+
Expect<Equal<Fn2, T2>>, //
|
|
69
|
+
Expect<Equal<Fn3, T3>> //
|
|
53
70
|
];
|
|
54
|
-
|
|
55
|
-
type cases = [Expect<Equal<M, MapTo<I, O>>>];
|
|
56
|
-
const cases: cases = [true];
|
|
57
|
-
expect(cases).toBe(cases);
|
|
71
|
+
const cases: cases = [true, true, true, true];
|
|
58
72
|
});
|
|
59
73
|
|
|
60
|
-
it("
|
|
61
|
-
|
|
62
|
-
type
|
|
63
|
-
type
|
|
64
|
-
|
|
65
|
-
|
|
74
|
+
it("MapFn<X> with I -> O[] variable config", () => {
|
|
75
|
+
type Fn0 = MapFn<I, O, "req", "I -> O[]", "opt">;
|
|
76
|
+
type Fn1 = MapFn<I, O, "req", "I -> O[]", "req">;
|
|
77
|
+
type Fn2 = MapFn<I, O, "opt", "I -> O[]", "opt">;
|
|
78
|
+
type Fn3 = MapFn<I, O, "opt", "I -> O[]", "req">;
|
|
79
|
+
|
|
80
|
+
type T0 = <S extends I | I[]>(source: S) => S extends I[] ? O[] : O[] | null;
|
|
81
|
+
type T1 = <S extends I | I[]>(source: S) => S extends I[] ? [O, ...O[]] : O[];
|
|
82
|
+
type T2 = <S extends I | I[] | undefined>(source?: S) => S extends I[] ? O[] : O[] | null;
|
|
83
|
+
type T3 = <S extends I | I[] | undefined>(source?: S) => S extends I[] ? [O, ...O[]] : O[];
|
|
84
|
+
|
|
85
|
+
type cases = [
|
|
86
|
+
Expect<Equal<Fn0, T0>>, //
|
|
87
|
+
Expect<Equal<Fn1, T1>>, //
|
|
88
|
+
Expect<Equal<Fn2, T2>>, //
|
|
89
|
+
Expect<Equal<Fn3, T3>> //
|
|
90
|
+
];
|
|
91
|
+
const cases: cases = [true, true, true, true];
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("MapFn<X> with I[] -> O variable config", () => {
|
|
95
|
+
type Fn0 = MapFn<I, O, "req", "I[] -> O", "opt">;
|
|
96
|
+
type Fn1 = MapFn<I, O, "req", "I[] -> O", "req">;
|
|
97
|
+
type Fn2 = MapFn<I, O, "opt", "I[] -> O", "opt">;
|
|
98
|
+
type Fn3 = MapFn<I, O, "opt", "I[] -> O", "req">;
|
|
99
|
+
|
|
100
|
+
type T0 = <S extends I[] | I[][]>(source: S) => S extends I[][] ? O[] : O | null;
|
|
101
|
+
type T1 = <S extends I[] | I[][]>(source: S) => S extends I[][] ? [O, ...O[]] : O;
|
|
102
|
+
type T2 = <S extends I[] | I[][] | undefined>(source?: S) => S extends I[][] ? O[] : O | null;
|
|
103
|
+
type T3 = <S extends I[] | I[][] | undefined>(source?: S) => S extends I[][] ? [O, ...O[]] : O;
|
|
104
|
+
|
|
105
|
+
type cases = [
|
|
106
|
+
Expect<Equal<Fn0, T0>>, //
|
|
107
|
+
Expect<Equal<Fn1, T1>>, //
|
|
108
|
+
Expect<Equal<Fn2, T2>>, //
|
|
109
|
+
Expect<Equal<Fn3, T3>> //
|
|
110
|
+
];
|
|
111
|
+
const cases: cases = [true, true, true, true];
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it("walk through of mapTo types", () => {
|
|
115
|
+
const t1 = mapTo<I, O>((i) => [{ title: i.title, count: i.products.length }]);
|
|
116
|
+
type T1 = typeof t1;
|
|
117
|
+
|
|
118
|
+
const a1 = mapTo
|
|
119
|
+
.config({ input: "req" })
|
|
120
|
+
.map<I, O>((i) => [{ title: i.title, count: i.products.length }]);
|
|
121
|
+
type A1 = typeof a1;
|
|
122
|
+
|
|
123
|
+
type cases = [
|
|
124
|
+
Expect<Equal<T1, MapFn<I, O>>>, //
|
|
125
|
+
Expect<Equal<T1, A1>>
|
|
126
|
+
];
|
|
127
|
+
const cases: cases = [true, true];
|
|
66
128
|
});
|
|
67
129
|
});
|
|
68
130
|
|
|
69
131
|
describe("mapTo() utility function", () => {
|
|
70
|
-
it("
|
|
71
|
-
const m = mapTo
|
|
72
|
-
{
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
132
|
+
it("M:1 conversion", () => {
|
|
133
|
+
const m = mapTo
|
|
134
|
+
.config({
|
|
135
|
+
direction: "I[] -> O",
|
|
136
|
+
output: "req",
|
|
137
|
+
})
|
|
138
|
+
.map<I, O>((i) => ({
|
|
139
|
+
title: "count of products",
|
|
140
|
+
count: i.reduce((acc, i) => (acc = acc + i.products.length), 0),
|
|
141
|
+
}));
|
|
142
|
+
|
|
143
|
+
const summary = m([i, i2]);
|
|
144
|
+
expect(summary.title).toBe("count of products");
|
|
145
|
+
expect(summary.count).toBe(6);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("1:1 conversion", () => {
|
|
149
|
+
const m = mapTo
|
|
150
|
+
.config({ output: "req", direction: MapDirection.OneToOne })
|
|
151
|
+
.map<I, O>((i) => ({ title: i.title, count: i.products.length }));
|
|
152
|
+
const o = m(i);
|
|
153
|
+
|
|
154
|
+
expect(o?.title).toBe(i.title);
|
|
155
|
+
expect(o?.count).toBe(3);
|
|
86
156
|
});
|
|
87
157
|
|
|
88
|
-
it("
|
|
89
|
-
const m = mapTo
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
]);
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
expect(
|
|
158
|
+
it("1:1 with 1:M conversion setting", () => {
|
|
159
|
+
const m = mapTo
|
|
160
|
+
.config({ output: "req", direction: MapDirection.OneToMany })
|
|
161
|
+
.map<I, O>((i) => [{ title: i.title, count: i.products.length }]);
|
|
162
|
+
const o = m(i);
|
|
163
|
+
|
|
164
|
+
expect(o.length).toBe(1);
|
|
165
|
+
expect(o[0]?.title).toBe(i.title);
|
|
166
|
+
expect(o[0]?.count).toBe(3);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
it("1:M conversion ", () => {
|
|
170
|
+
const m = mapTo
|
|
171
|
+
.config({ output: "req", direction: MapDirection.OneToMany })
|
|
172
|
+
.map<I, O>((i) => [{ title: i.title, count: i.products.length }]);
|
|
173
|
+
const o = m([i, i2]);
|
|
174
|
+
|
|
175
|
+
expect(o.length).toBe(2);
|
|
176
|
+
o.map((item) => expect("title" in item).toBeTruthy());
|
|
106
177
|
});
|
|
107
178
|
|
|
108
|
-
it("
|
|
109
|
-
const m = mapTo<I, O>((
|
|
110
|
-
return
|
|
111
|
-
|
|
112
|
-
:
|
|
113
|
-
|
|
114
|
-
title: i.title,
|
|
115
|
-
count: i.products.length,
|
|
116
|
-
},
|
|
117
|
-
];
|
|
179
|
+
it("M:1 conversion", () => {
|
|
180
|
+
const m = mapTo.config({ output: "req", direction: MapDirection.ManyToOne }).map<I, O>((i) => {
|
|
181
|
+
return {
|
|
182
|
+
title: "summary",
|
|
183
|
+
count: i.reduce((acc, i) => (acc = acc + i.products.length), 0),
|
|
184
|
+
};
|
|
118
185
|
});
|
|
119
|
-
const
|
|
186
|
+
const o = m([i, i2]);
|
|
120
187
|
|
|
121
|
-
expect(
|
|
122
|
-
expect(
|
|
188
|
+
expect(o.title).toBe("summary");
|
|
189
|
+
expect(o.count).toBe(6);
|
|
123
190
|
});
|
|
124
191
|
});
|