inferred-types 0.23.4 → 0.24.1
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/dist/index.d.ts +38 -9
- package/dist/index.js +42 -2
- package/dist/index.mjs +41 -2
- package/package.json +1 -1
- package/src/types/dictionary/props.ts +17 -7
- package/src/types/type-conversion/Widen.ts +7 -0
- package/src/types/type-conversion/index.ts +1 -0
- package/src/utility/dictionary/arrayToKeyLookup.ts +3 -1
- package/src/utility/dictionary/dictArr.ts +69 -0
- package/src/utility/dictionary/index.ts +1 -0
- package/tests/dictionary/OptionalKeys.test.ts +24 -0
- package/tests/dictionary/RequiredKeys.test.ts +53 -0
- package/tests/lists/UniqueForProp.spec.ts +46 -13
- package/tests/lists/dictArr.test.ts +110 -0
package/dist/index.d.ts
CHANGED
|
@@ -667,20 +667,22 @@ declare type SpecialCharacters = "@" | "~" | "^" | "#" | "&" | "*";
|
|
|
667
667
|
declare type NonAlpha = Whitespace | Punctuation | NumericString | Bracket | SpecialCharacters;
|
|
668
668
|
|
|
669
669
|
/**
|
|
670
|
-
* Extracts the _required_ keys in the object's type
|
|
670
|
+
* Extracts the _required_ keys in the object's type. You also may
|
|
671
|
+
* optionally filter by the _value_ of the key.
|
|
671
672
|
*/
|
|
672
|
-
declare type RequiredKeys<T extends object> = {
|
|
673
|
+
declare type RequiredKeys<T extends object, V extends any = any> = {
|
|
673
674
|
[K in keyof T]-?: {} extends {
|
|
674
675
|
[P in K]: T[K];
|
|
675
|
-
} ? never : K;
|
|
676
|
+
} ? never : T[K] extends V ? K : never;
|
|
676
677
|
}[keyof T];
|
|
677
678
|
/**
|
|
678
|
-
* Extracts the _optional_ keys in the object's type
|
|
679
|
+
* Extracts the _optional_ keys in the object's type. You also may
|
|
680
|
+
* optionally filter by the _value_ of the key.
|
|
679
681
|
*/
|
|
680
|
-
declare type OptionalKeys<T extends object> = {
|
|
682
|
+
declare type OptionalKeys<T extends object, V extends any = any> = {
|
|
681
683
|
[K in keyof T]-?: {} extends {
|
|
682
684
|
[P in K]: T[K];
|
|
683
|
-
} ? K : never;
|
|
685
|
+
} ? V extends T[K] ? K : never : never;
|
|
684
686
|
}[keyof T];
|
|
685
687
|
/**
|
|
686
688
|
* The _keys_ on a given object `T` which have a literal value of `W`.
|
|
@@ -743,7 +745,7 @@ declare type RequiredProps<T extends object> = Pick<T, RequiredKeys<T>>;
|
|
|
743
745
|
*
|
|
744
746
|
* Reduces an object to only key/value pairs where the key is optional
|
|
745
747
|
*/
|
|
746
|
-
declare type OptionalProps<T extends object> = Pick<T,
|
|
748
|
+
declare type OptionalProps<T extends object> = Pick<T, OptionalKeys<T>>;
|
|
747
749
|
/**
|
|
748
750
|
* **WithValue**
|
|
749
751
|
*
|
|
@@ -1320,6 +1322,8 @@ declare type FirstOfEach<T extends readonly any[][]> = T[number][0] extends T[nu
|
|
|
1320
1322
|
*/
|
|
1321
1323
|
declare type TupleToUnion<T> = Mutable<T> extends any[] ? Mutable<T>[number] : never;
|
|
1322
1324
|
|
|
1325
|
+
declare type Widen<T> = T extends string ? string : T extends number ? number : T extends boolean ? boolean : T;
|
|
1326
|
+
|
|
1323
1327
|
/**
|
|
1324
1328
|
* Typescript utility which receives `T` as shape which resembles `DictArray<D>`
|
|
1325
1329
|
* and if the type `D` can be inferred it is returned.
|
|
@@ -1380,7 +1384,9 @@ declare const api: <N extends Narrowable, TPrivate extends Readonly<Record<any,
|
|
|
1380
1384
|
* Passing in an array of strings, you are passed back a dictionary with
|
|
1381
1385
|
* all the keys being the strings and values set to `true`.
|
|
1382
1386
|
* ```ts
|
|
1383
|
-
* // {
|
|
1387
|
+
* // { bar: true, bar: true } as const;
|
|
1388
|
+
* const d - dictArr(arr);
|
|
1389
|
+
*
|
|
1384
1390
|
* const fooBar = arrayToKeyLookup("foo", "bar");
|
|
1385
1391
|
* ```
|
|
1386
1392
|
*/
|
|
@@ -1408,6 +1414,29 @@ declare function defineProperties<T extends {}>(obj: T): DefinePropertiesApi<T>;
|
|
|
1408
1414
|
*/
|
|
1409
1415
|
declare function dictionaryTransform<I extends object, O extends SameKeys<I>>(input: I, transform: Transformer<I, O>): O;
|
|
1410
1416
|
|
|
1417
|
+
interface Uniqueness<T> {
|
|
1418
|
+
/** boolean flag to indicate whether the property was unique across all records */
|
|
1419
|
+
isUnique: boolean;
|
|
1420
|
+
/** the overall number of records which contained the property */
|
|
1421
|
+
size: number;
|
|
1422
|
+
/** specifies if undefined values were encountered for this property */
|
|
1423
|
+
includedUndefined: boolean;
|
|
1424
|
+
/** the unique values for the property across all records */
|
|
1425
|
+
values: readonly T[];
|
|
1426
|
+
}
|
|
1427
|
+
declare type DictArrApi<T extends Record<string, Narrowable>, A extends readonly T[]> = {
|
|
1428
|
+
length: number;
|
|
1429
|
+
toLookup<PL extends RequiredKeys<T, string> & keyof T & string>(prop: PL): UniqueForProp<A, PL> extends string ? Record<UniqueForProp<A, PL>, T> : Record<string, T>;
|
|
1430
|
+
sum<PS extends RequiredKeys<T, number> | OptionalKeys<T, number>>(prop: PS): number;
|
|
1431
|
+
count<PC extends OptionalKeys<T>>(prop: PC): number;
|
|
1432
|
+
unique<PU extends Keys<T> & keyof T>(prop: PU): Uniqueness<T[PU]>;
|
|
1433
|
+
};
|
|
1434
|
+
/**
|
|
1435
|
+
* converts an array of objects to a dictionary with keys formed from a given property
|
|
1436
|
+
* of the object and the value being the object itself.
|
|
1437
|
+
*/
|
|
1438
|
+
declare const dictArr: <T extends Record<string, Narrowable>>(...dicts: readonly T[]) => DictArrApi<T, readonly T[]>;
|
|
1439
|
+
|
|
1411
1440
|
/**
|
|
1412
1441
|
* **entries**
|
|
1413
1442
|
*
|
|
@@ -1741,4 +1770,4 @@ interface IFluentConfigurator<C> {
|
|
|
1741
1770
|
*/
|
|
1742
1771
|
declare function FluentConfigurator<I>(initial?: I): IFluentConfigurator<{}>;
|
|
1743
1772
|
|
|
1744
|
-
export { AllCaps, Alpha, AlphaNumeric, Api, ApiFunction, ApiValue, AppendToDictionary, AppendToObject, ArrConcat, Array$1 as Array, ArrayConverter, AssertExtends, Bracket, Break, CamelCase, CapitalizeWords, ClosingBracket, Condition, Configurator, Constructor, DashToSnake, DashUppercase, Dasherize, DefinePropertiesApi, DictArray, DictArrayFilterCallback, DictArrayKv, DictChangeValue, DictFromKv, DictKvTuple, DictPartialApplication, DictPrependWithFn, DictReturnValues, DynamicRule, DynamicRuleSet, Email, EnumValues, ExpandRecursively, ExpectExtends, ExplicitFunction, ExtendsClause, ExtendsNarrowlyClause, Filter, FinalReturn, First, FirstKey, FirstKeyValue, FirstOfEach, FluentConfigurator, FluentFunction, FromDictArray, FunctionType, GeneralDictionary, Get, HasUppercase, IConfigurator, IFluentConfigurator, If, IfExtends, IfExtendsThen, Include, Includes, IsArray, IsBoolean, IsCapitalized, IsFalse, IsFunction, IsObject, IsString, IsTrue, KebabCase, KeyValue, KeyedRecord, Keys, KeysWithValue, KvFrom, KvTuple, LastInUnion, LeftWhitespace, Length, LowerAllCaps, LowerAlpha, MapTo, MaybeFalse, MaybeTrue, Model, Mutable, MutableProps, MutationFunction, MutationIdentity, Narrowable, NonAlpha, NonNumericKeys, NonStringKeys, Not, Numeric, NumericKeys, NumericString, ObjectType, Opaque, OpeningBracket, 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, StringKeys, StringLength, ToFluent, Transformer, Trim, TrimLeft, TrimRight, TupleToUnion, Type, TypeApi, TypeCondition, TypeDefinition, TypeGuard, TypeOptions, UnionToIntersection, UnionToTuple, UniqueDictionary, UniqueForProp, UpperAlpha, ValueTuple, ValueTypeFunc, ValueTypes, Where, WhereNot, Whitespace, WithNumericKeys, WithStringKeys, WithValue, ZipCode, and, api, arrayToKeyLookup, arrayToObject, condition, createFnWithProps, createMutationFunction, defineProperties, defineType, dictToKv, dictionaryTransform, entries, equals, filterDictArray, fnWithProps, greater, groupBy, idLiteral, idTypeGuard, identity, ifTypeOf, isArray, isBoolean, isFalse, isFunction, isNull, isNumber, isObject, isString, isSymbol, isTrue, isType, isUndefined, keys, kindLiteral, kv, kvToDict, less, literal, mapTo, mapValues, nameLiteral, or, randomString, readonlyFnWithProps, ruleSet, strArrayToDict, type, typeApi, uuid, valueTypes, withValue };
|
|
1773
|
+
export { AllCaps, Alpha, AlphaNumeric, Api, ApiFunction, ApiValue, AppendToDictionary, AppendToObject, ArrConcat, Array$1 as Array, ArrayConverter, AssertExtends, Bracket, Break, CamelCase, CapitalizeWords, ClosingBracket, Condition, 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, Filter, FinalReturn, First, FirstKey, FirstKeyValue, FirstOfEach, FluentConfigurator, FluentFunction, FromDictArray, FunctionType, GeneralDictionary, Get, HasUppercase, IConfigurator, IFluentConfigurator, If, IfExtends, IfExtendsThen, Include, Includes, IsArray, IsBoolean, IsCapitalized, IsFalse, IsFunction, IsObject, IsString, IsTrue, KebabCase, KeyValue, KeyedRecord, Keys, KeysWithValue, KvFrom, KvTuple, LastInUnion, LeftWhitespace, Length, LowerAllCaps, LowerAlpha, MapTo, MaybeFalse, MaybeTrue, Model, Mutable, MutableProps, MutationFunction, MutationIdentity, Narrowable, NonAlpha, NonNumericKeys, NonStringKeys, Not, Numeric, NumericKeys, NumericString, ObjectType, Opaque, OpeningBracket, 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, StringKeys, StringLength, ToFluent, Transformer, Trim, TrimLeft, TrimRight, TupleToUnion, Type, TypeApi, TypeCondition, TypeDefinition, TypeGuard, TypeOptions, UnionToIntersection, UnionToTuple, UniqueDictionary, UniqueForProp, Uniqueness, UpperAlpha, ValueTuple, ValueTypeFunc, ValueTypes, Where, WhereNot, Whitespace, Widen, WithNumericKeys, WithStringKeys, WithValue, ZipCode, and, api, arrayToKeyLookup, arrayToObject, condition, createFnWithProps, createMutationFunction, defineProperties, defineType, dictArr, dictToKv, dictionaryTransform, entries, equals, filterDictArray, fnWithProps, greater, groupBy, idLiteral, idTypeGuard, identity, ifTypeOf, isArray, isBoolean, isFalse, isFunction, isNull, isNumber, isObject, isString, isSymbol, isTrue, isType, isUndefined, keys, kindLiteral, kv, kvToDict, less, literal, mapTo, mapValues, nameLiteral, or, randomString, readonlyFnWithProps, ruleSet, strArrayToDict, type, typeApi, uuid, valueTypes, withValue };
|
package/dist/index.js
CHANGED
|
@@ -34,6 +34,7 @@ __export(src_exports, {
|
|
|
34
34
|
createMutationFunction: () => createMutationFunction,
|
|
35
35
|
defineProperties: () => defineProperties,
|
|
36
36
|
defineType: () => defineType,
|
|
37
|
+
dictArr: () => dictArr,
|
|
37
38
|
dictToKv: () => dictToKv,
|
|
38
39
|
dictionaryTransform: () => dictionaryTransform,
|
|
39
40
|
entries: () => entries,
|
|
@@ -203,6 +204,44 @@ function dictionaryTransform(input, transform) {
|
|
|
203
204
|
}, {});
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
// src/utility/dictionary/dictArr.ts
|
|
208
|
+
var dictArr = (...dicts) => {
|
|
209
|
+
const api2 = {
|
|
210
|
+
length: dicts.length,
|
|
211
|
+
toLookup: (prop) => {
|
|
212
|
+
let dict = {};
|
|
213
|
+
for (const obj of dicts) {
|
|
214
|
+
const key = obj[prop];
|
|
215
|
+
dict = { ...dict, [key]: obj };
|
|
216
|
+
}
|
|
217
|
+
return dict;
|
|
218
|
+
},
|
|
219
|
+
unique: (prop) => {
|
|
220
|
+
const v = /* @__PURE__ */ new Set();
|
|
221
|
+
dicts.forEach((i) => v.add(i[prop]));
|
|
222
|
+
const size = dictArr(...dicts).count(prop);
|
|
223
|
+
const values = Array.from(v);
|
|
224
|
+
const u = {
|
|
225
|
+
isUnique: values.includes(void 0) ? size === v.size - 1 : size === v.size,
|
|
226
|
+
includedUndefined: values.includes(void 0) ? true : false,
|
|
227
|
+
size,
|
|
228
|
+
values
|
|
229
|
+
};
|
|
230
|
+
return u;
|
|
231
|
+
},
|
|
232
|
+
sum: (prop) => {
|
|
233
|
+
return dicts.reduce((acc, obj) => prop in obj ? acc + obj[prop] : acc, 0);
|
|
234
|
+
},
|
|
235
|
+
count: (prop) => {
|
|
236
|
+
return dicts.reduce(
|
|
237
|
+
(acc, obj) => prop in obj && typeof obj[prop] !== "undefined" ? acc + 1 : acc,
|
|
238
|
+
0
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
return api2;
|
|
243
|
+
};
|
|
244
|
+
|
|
206
245
|
// src/utility/dictionary/entries.ts
|
|
207
246
|
function entries(obj) {
|
|
208
247
|
const iterable = {
|
|
@@ -293,9 +332,9 @@ function FluentConfigurator(initial = {}) {
|
|
|
293
332
|
}
|
|
294
333
|
|
|
295
334
|
// src/utility/dictionary/kv/filterDictArray.ts
|
|
296
|
-
function filterDictArray(
|
|
335
|
+
function filterDictArray(dictArr2, cb) {
|
|
297
336
|
const state = Configurator();
|
|
298
|
-
const updated =
|
|
337
|
+
const updated = dictArr2.filter((i) => {
|
|
299
338
|
const [k, v] = i;
|
|
300
339
|
const keep = cb(k, v);
|
|
301
340
|
if (!keep) {
|
|
@@ -639,6 +678,7 @@ function withValue(td) {
|
|
|
639
678
|
createMutationFunction,
|
|
640
679
|
defineProperties,
|
|
641
680
|
defineType,
|
|
681
|
+
dictArr,
|
|
642
682
|
dictToKv,
|
|
643
683
|
dictionaryTransform,
|
|
644
684
|
entries,
|
package/dist/index.mjs
CHANGED
|
@@ -121,6 +121,44 @@ function dictionaryTransform(input, transform) {
|
|
|
121
121
|
}, {});
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
+
// src/utility/dictionary/dictArr.ts
|
|
125
|
+
var dictArr = (...dicts) => {
|
|
126
|
+
const api2 = {
|
|
127
|
+
length: dicts.length,
|
|
128
|
+
toLookup: (prop) => {
|
|
129
|
+
let dict = {};
|
|
130
|
+
for (const obj of dicts) {
|
|
131
|
+
const key = obj[prop];
|
|
132
|
+
dict = { ...dict, [key]: obj };
|
|
133
|
+
}
|
|
134
|
+
return dict;
|
|
135
|
+
},
|
|
136
|
+
unique: (prop) => {
|
|
137
|
+
const v = /* @__PURE__ */ new Set();
|
|
138
|
+
dicts.forEach((i) => v.add(i[prop]));
|
|
139
|
+
const size = dictArr(...dicts).count(prop);
|
|
140
|
+
const values = Array.from(v);
|
|
141
|
+
const u = {
|
|
142
|
+
isUnique: values.includes(void 0) ? size === v.size - 1 : size === v.size,
|
|
143
|
+
includedUndefined: values.includes(void 0) ? true : false,
|
|
144
|
+
size,
|
|
145
|
+
values
|
|
146
|
+
};
|
|
147
|
+
return u;
|
|
148
|
+
},
|
|
149
|
+
sum: (prop) => {
|
|
150
|
+
return dicts.reduce((acc, obj) => prop in obj ? acc + obj[prop] : acc, 0);
|
|
151
|
+
},
|
|
152
|
+
count: (prop) => {
|
|
153
|
+
return dicts.reduce(
|
|
154
|
+
(acc, obj) => prop in obj && typeof obj[prop] !== "undefined" ? acc + 1 : acc,
|
|
155
|
+
0
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
return api2;
|
|
160
|
+
};
|
|
161
|
+
|
|
124
162
|
// src/utility/dictionary/entries.ts
|
|
125
163
|
function entries(obj) {
|
|
126
164
|
const iterable = {
|
|
@@ -211,9 +249,9 @@ function FluentConfigurator(initial = {}) {
|
|
|
211
249
|
}
|
|
212
250
|
|
|
213
251
|
// src/utility/dictionary/kv/filterDictArray.ts
|
|
214
|
-
function filterDictArray(
|
|
252
|
+
function filterDictArray(dictArr2, cb) {
|
|
215
253
|
const state = Configurator();
|
|
216
|
-
const updated =
|
|
254
|
+
const updated = dictArr2.filter((i) => {
|
|
217
255
|
const [k, v] = i;
|
|
218
256
|
const keep = cb(k, v);
|
|
219
257
|
if (!keep) {
|
|
@@ -556,6 +594,7 @@ export {
|
|
|
556
594
|
createMutationFunction,
|
|
557
595
|
defineProperties,
|
|
558
596
|
defineType,
|
|
597
|
+
dictArr,
|
|
559
598
|
dictToKv,
|
|
560
599
|
dictionaryTransform,
|
|
561
600
|
entries,
|
package/package.json
CHANGED
|
@@ -1,17 +1,27 @@
|
|
|
1
1
|
import { Alpha } from "../alphabetic/alpha-characters";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Extracts the _required_ keys in the object's type
|
|
4
|
+
* Extracts the _required_ keys in the object's type. You also may
|
|
5
|
+
* optionally filter by the _value_ of the key.
|
|
5
6
|
*/
|
|
6
|
-
export type RequiredKeys<T extends object> = {
|
|
7
|
-
[K in keyof T]-?: {} extends { [P in K]: T[K] }
|
|
7
|
+
export type RequiredKeys<T extends object, V extends any = any> = {
|
|
8
|
+
[K in keyof T]-?: {} extends { [P in K]: T[K] }
|
|
9
|
+
? never //
|
|
10
|
+
: T[K] extends V
|
|
11
|
+
? K
|
|
12
|
+
: never;
|
|
8
13
|
}[keyof T];
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
|
-
* Extracts the _optional_ keys in the object's type
|
|
16
|
+
* Extracts the _optional_ keys in the object's type. You also may
|
|
17
|
+
* optionally filter by the _value_ of the key.
|
|
12
18
|
*/
|
|
13
|
-
export type OptionalKeys<T extends object> = {
|
|
14
|
-
[K in keyof T]-?: {} extends { [P in K]: T[K] }
|
|
19
|
+
export type OptionalKeys<T extends object, V extends any = any> = {
|
|
20
|
+
[K in keyof T]-?: {} extends { [P in K]: T[K] }
|
|
21
|
+
? V extends T[K]
|
|
22
|
+
? K
|
|
23
|
+
: never //
|
|
24
|
+
: never;
|
|
15
25
|
}[keyof T];
|
|
16
26
|
|
|
17
27
|
/**
|
|
@@ -84,7 +94,7 @@ export type RequiredProps<T extends object> = Pick<T, RequiredKeys<T>>;
|
|
|
84
94
|
*
|
|
85
95
|
* Reduces an object to only key/value pairs where the key is optional
|
|
86
96
|
*/
|
|
87
|
-
export type OptionalProps<T extends object> = Pick<T,
|
|
97
|
+
export type OptionalProps<T extends object> = Pick<T, OptionalKeys<T>>;
|
|
88
98
|
|
|
89
99
|
/**
|
|
90
100
|
* **WithValue**
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* Passing in an array of strings, you are passed back a dictionary with
|
|
3
3
|
* all the keys being the strings and values set to `true`.
|
|
4
4
|
* ```ts
|
|
5
|
-
* // {
|
|
5
|
+
* // { bar: true, bar: true } as const;
|
|
6
|
+
* const d - dictArr(arr);
|
|
7
|
+
*
|
|
6
8
|
* const fooBar = arrayToKeyLookup("foo", "bar");
|
|
7
9
|
* ```
|
|
8
10
|
*/
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Keys, Narrowable, OptionalKeys, RequiredKeys, UniqueForProp } from "src/types";
|
|
2
|
+
|
|
3
|
+
export interface Uniqueness<T> {
|
|
4
|
+
/** boolean flag to indicate whether the property was unique across all records */
|
|
5
|
+
isUnique: boolean;
|
|
6
|
+
/** the overall number of records which contained the property */
|
|
7
|
+
size: number;
|
|
8
|
+
/** specifies if undefined values were encountered for this property */
|
|
9
|
+
includedUndefined: boolean;
|
|
10
|
+
/** the unique values for the property across all records */
|
|
11
|
+
values: readonly T[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export type DictArrApi<T extends Record<string, Narrowable>, A extends readonly T[]> = {
|
|
15
|
+
length: number;
|
|
16
|
+
toLookup<PL extends RequiredKeys<T, string> & keyof T & string>(
|
|
17
|
+
prop: PL
|
|
18
|
+
): UniqueForProp<A, PL> extends string ? Record<UniqueForProp<A, PL>, T> : Record<string, T>;
|
|
19
|
+
sum<PS extends RequiredKeys<T, number> | OptionalKeys<T, number>>(prop: PS): number;
|
|
20
|
+
count<PC extends OptionalKeys<T>>(prop: PC): number;
|
|
21
|
+
unique<PU extends Keys<T> & keyof T>(prop: PU): Uniqueness<T[PU]>;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* converts an array of objects to a dictionary with keys formed from a given property
|
|
26
|
+
* of the object and the value being the object itself.
|
|
27
|
+
*/
|
|
28
|
+
export const dictArr = <T extends Record<string, Narrowable>>(...dicts: readonly T[]) => {
|
|
29
|
+
// build API
|
|
30
|
+
const api: DictArrApi<T, typeof dicts> = {
|
|
31
|
+
length: dicts.length,
|
|
32
|
+
toLookup: (prop) => {
|
|
33
|
+
let dict: Record<string, T> = {};
|
|
34
|
+
for (const obj of dicts) {
|
|
35
|
+
const key = obj[prop] as string;
|
|
36
|
+
dict = { ...dict, [key]: obj };
|
|
37
|
+
}
|
|
38
|
+
return dict;
|
|
39
|
+
},
|
|
40
|
+
/**
|
|
41
|
+
* The unique values of a given property
|
|
42
|
+
*/
|
|
43
|
+
unique: (prop) => {
|
|
44
|
+
type P = typeof prop;
|
|
45
|
+
const v = new Set<T[P]>();
|
|
46
|
+
dicts.forEach((i) => v.add(i[prop]));
|
|
47
|
+
const size = dictArr(...dicts).count(prop as any);
|
|
48
|
+
const values = Array.from(v);
|
|
49
|
+
const u: Uniqueness<T[P]> = {
|
|
50
|
+
isUnique: values.includes(undefined as any) ? size === v.size - 1 : size === v.size,
|
|
51
|
+
includedUndefined: values.includes(undefined as any) ? true : false,
|
|
52
|
+
size,
|
|
53
|
+
values,
|
|
54
|
+
};
|
|
55
|
+
return u;
|
|
56
|
+
},
|
|
57
|
+
sum: (prop) => {
|
|
58
|
+
return dicts.reduce((acc, obj) => (prop in obj ? acc + (obj[prop] as number) : acc), 0);
|
|
59
|
+
},
|
|
60
|
+
count: (prop) => {
|
|
61
|
+
return dicts.reduce(
|
|
62
|
+
(acc, obj) => (prop in obj && typeof obj[prop] !== "undefined" ? acc + 1 : acc),
|
|
63
|
+
0
|
|
64
|
+
);
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return api;
|
|
69
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import type { Expect, Equal } from "@type-challenges/utils";
|
|
3
|
+
import { OptionalKeys } from "src/types/dictionary";
|
|
4
|
+
|
|
5
|
+
type Test = { title: string; value: number; color?: string };
|
|
6
|
+
|
|
7
|
+
describe("OptionalKeys<T, V>", () => {
|
|
8
|
+
it("basic usage without filtering on value", () => {
|
|
9
|
+
type T = OptionalKeys<Test>;
|
|
10
|
+
|
|
11
|
+
type cases = [Expect<Equal<T, "color">>];
|
|
12
|
+
const cases: cases = [true];
|
|
13
|
+
expect(cases).toBe(cases);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("basic usage with a value filter", () => {
|
|
17
|
+
type T1 = OptionalKeys<Test, string>;
|
|
18
|
+
type T2 = OptionalKeys<Test, number>;
|
|
19
|
+
|
|
20
|
+
type cases = [Expect<Equal<T1, "color">>, Expect<Equal<T2, never>>];
|
|
21
|
+
const cases: cases = [true, true];
|
|
22
|
+
expect(cases).toBe(cases);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import type { Expect, Equal } from "@type-challenges/utils";
|
|
3
|
+
import { RequiredKeys } from "src/types/dictionary";
|
|
4
|
+
import { First, Narrowable } from "src";
|
|
5
|
+
|
|
6
|
+
type Test = { title: string; value: number; color?: string };
|
|
7
|
+
|
|
8
|
+
describe("RequiredKeys<T, V>", () => {
|
|
9
|
+
it("basic usage without filtering on value", () => {
|
|
10
|
+
type T = RequiredKeys<Test>;
|
|
11
|
+
|
|
12
|
+
type cases = [Expect<Equal<T, "title" | "value">>];
|
|
13
|
+
const cases: cases = [true];
|
|
14
|
+
expect(cases).toBe(cases);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("basic usage with a value filter", () => {
|
|
18
|
+
type T1 = RequiredKeys<Test, string>;
|
|
19
|
+
type T2 = RequiredKeys<Test, number>;
|
|
20
|
+
|
|
21
|
+
type cases = [Expect<Equal<T1, "title">>, Expect<Equal<T2, "value">>];
|
|
22
|
+
const cases: cases = [true, true];
|
|
23
|
+
expect(cases).toBe(cases);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it("typed explicitly", () => {
|
|
27
|
+
type O = {
|
|
28
|
+
id: number;
|
|
29
|
+
name: string;
|
|
30
|
+
title: string;
|
|
31
|
+
cost?: number;
|
|
32
|
+
color?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const genericArr: O[] = [
|
|
36
|
+
{ id: 1, name: "foo", title: "one", cost: 15 },
|
|
37
|
+
{ id: 2, name: "bar", title: "one" },
|
|
38
|
+
{ id: 3, name: "baz", title: "two", cost: 45, color: "green" },
|
|
39
|
+
];
|
|
40
|
+
const fn = <T extends Record<string, Narrowable>, A extends readonly T[]>(arr: A): A => arr;
|
|
41
|
+
|
|
42
|
+
const v = fn(genericArr);
|
|
43
|
+
type V = typeof v;
|
|
44
|
+
type T1 = RequiredKeys<First<V>>;
|
|
45
|
+
type T2 = RequiredKeys<First<V>, string>;
|
|
46
|
+
|
|
47
|
+
type cases = [
|
|
48
|
+
Expect<Equal<T1, "id" | "name" | "title">>, //
|
|
49
|
+
Expect<Equal<T2, "name" | "title">>
|
|
50
|
+
];
|
|
51
|
+
const cases: cases = [true, true];
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -1,26 +1,59 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
|
|
3
|
-
import { Expect, Equal, ExpectExtends } from "@type-challenges/utils";
|
|
2
|
+
import { Expect, Equal } from "@type-challenges/utils";
|
|
4
3
|
import { UniqueForProp } from "src/types/lists";
|
|
5
4
|
|
|
5
|
+
const narrow_data = [
|
|
6
|
+
{ id: 123, color: "blue" },
|
|
7
|
+
{ id: 456, color: "red" },
|
|
8
|
+
] as const;
|
|
9
|
+
type NarrowData = typeof narrow_data;
|
|
10
|
+
|
|
11
|
+
type R = { id: number; color: string };
|
|
12
|
+
const wide_data: readonly R[] = [
|
|
13
|
+
{ id: 123, color: "blue" },
|
|
14
|
+
{ id: 456, color: "red" },
|
|
15
|
+
];
|
|
16
|
+
type WideData = typeof wide_data;
|
|
17
|
+
|
|
18
|
+
type R2<T extends string> = Readonly<{ id: number; color: T }>;
|
|
19
|
+
const hybrid = <H extends R2<any>>(...data: H[]): readonly H[] => data;
|
|
20
|
+
const hybrid_data = hybrid(
|
|
21
|
+
...[
|
|
22
|
+
{ id: 123, color: "blue" },
|
|
23
|
+
{ id: 456, color: "red" },
|
|
24
|
+
]
|
|
25
|
+
);
|
|
26
|
+
type HybridData = typeof hybrid_data;
|
|
27
|
+
|
|
6
28
|
describe("UniqueForProp<T, P>", () => {
|
|
7
|
-
it("
|
|
8
|
-
|
|
9
|
-
{ id: 123, color: "blue" },
|
|
10
|
-
{ id: 456, color: "red" },
|
|
11
|
-
] as const;
|
|
12
|
-
type Data = typeof data;
|
|
13
|
-
type U = UniqueForProp<Data, "id">;
|
|
29
|
+
it("narrow data works as expected", () => {
|
|
30
|
+
type U = UniqueForProp<NarrowData, "id">;
|
|
14
31
|
|
|
15
32
|
type cases = [
|
|
16
33
|
// the expected keys are part of the union
|
|
17
|
-
Expect<ExpectExtends<U, 123>>,
|
|
18
|
-
Expect<ExpectExtends<U, 456>>,
|
|
19
|
-
// but so is a lot other junk
|
|
20
34
|
Expect<Equal<U, 123 | 456>>
|
|
21
35
|
];
|
|
22
36
|
|
|
23
|
-
const c: cases = [true
|
|
37
|
+
const c: cases = [true];
|
|
38
|
+
expect(c).toBe(c);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("wide data only knows the wide type", () => {
|
|
42
|
+
type U = UniqueForProp<WideData, "id">;
|
|
43
|
+
|
|
44
|
+
type cases = [
|
|
45
|
+
// the keys are rolled up the wide type
|
|
46
|
+
Expect<Equal<U, number>>
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
const c: cases = [true];
|
|
50
|
+
expect(c).toBe(c);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("wide data only knows the wide type", () => {
|
|
54
|
+
type U = UniqueForProp<HybridData, "id">;
|
|
55
|
+
type cases = [Expect<Equal<U, number>>];
|
|
56
|
+
const c: cases = [true];
|
|
24
57
|
expect(c).toBe(c);
|
|
25
58
|
});
|
|
26
59
|
});
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import type { Expect, Equal } from "@type-challenges/utils";
|
|
3
|
+
import { dictArr, keys, literal } from "src/utility";
|
|
4
|
+
|
|
5
|
+
type O = {
|
|
6
|
+
id: number;
|
|
7
|
+
name: string;
|
|
8
|
+
title: string;
|
|
9
|
+
cost?: number;
|
|
10
|
+
color?: string;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const genericArr: O[] = [
|
|
14
|
+
{ id: 1, name: "foo", title: "one", cost: 15 },
|
|
15
|
+
{ id: 2, name: "bar", title: "one" },
|
|
16
|
+
{ id: 3, name: "baz", title: "two", cost: 45, color: "green" },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const literalArr = [
|
|
20
|
+
literal({ id: 1, name: "foo", title: "one", cost: 15 }),
|
|
21
|
+
literal({ id: 2, name: "bar", title: "one" }),
|
|
22
|
+
literal({ id: 3, name: "baz", title: "two", cost: 45, color: "green" }),
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const wideArr = [
|
|
26
|
+
{ id: 1, name: "foo", title: "one", cost: 15 },
|
|
27
|
+
{ id: 2, name: "bar", title: "one" },
|
|
28
|
+
{ id: 3, name: "baz", title: "two", cost: 45, color: "green" },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
describe("dictArr() utility API", () => {
|
|
32
|
+
it("returns an API when passed the array", () => {
|
|
33
|
+
const r = dictArr(...genericArr);
|
|
34
|
+
type R = typeof r;
|
|
35
|
+
// runtime checks
|
|
36
|
+
expect(typeof r.toLookup).contains("function");
|
|
37
|
+
expect(typeof r.unique).contains("function");
|
|
38
|
+
expect(typeof r.count).contains("function");
|
|
39
|
+
expect(typeof r.sum).contains("function");
|
|
40
|
+
expect(typeof r.length).contains("number");
|
|
41
|
+
|
|
42
|
+
// type checks
|
|
43
|
+
type ToLookupParam = Parameters<R["toLookup"]>[0];
|
|
44
|
+
type UniqueParam = Parameters<R["unique"]>[0];
|
|
45
|
+
type CountParam = Parameters<R["count"]>[0];
|
|
46
|
+
type SumParam = Parameters<R["sum"]>[0];
|
|
47
|
+
|
|
48
|
+
// type ToLookupReturn = ReturnType<R["toLookup"]>;
|
|
49
|
+
|
|
50
|
+
type cases = [
|
|
51
|
+
Expect<Equal<ToLookupParam, "name" | "title">>,
|
|
52
|
+
Expect<Equal<UniqueParam, "id" | "name" | "title" | "cost" | "color">>,
|
|
53
|
+
Expect<Equal<CountParam, "cost" | "color">>,
|
|
54
|
+
Expect<Equal<SumParam, "id" | "cost">>
|
|
55
|
+
];
|
|
56
|
+
const cases: cases = [true, true, true, true];
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("toLookup() creates type-strong lookup", () => {
|
|
60
|
+
const g = dictArr(...genericArr);
|
|
61
|
+
const l = dictArr(...literalArr);
|
|
62
|
+
const pl = dictArr(...wideArr);
|
|
63
|
+
const byNameG = g.toLookup("name");
|
|
64
|
+
const byNameL = l.toLookup("name");
|
|
65
|
+
const byNamePL = pl.toLookup("name");
|
|
66
|
+
const kg = keys(byNameG);
|
|
67
|
+
const kl = keys(byNameL);
|
|
68
|
+
const kpl = keys(byNamePL);
|
|
69
|
+
expect(kg).contains("foo");
|
|
70
|
+
expect(kg).contains("bar");
|
|
71
|
+
expect(kg).contains("baz");
|
|
72
|
+
|
|
73
|
+
expect(kl).contains("foo");
|
|
74
|
+
expect(kl).contains("bar");
|
|
75
|
+
expect(kl).contains("baz");
|
|
76
|
+
|
|
77
|
+
expect(kpl).contains("foo");
|
|
78
|
+
expect(kpl).contains("bar");
|
|
79
|
+
expect(kpl).contains("baz");
|
|
80
|
+
|
|
81
|
+
const fooG = genericArr.find((i) => i.name === "foo");
|
|
82
|
+
expect(byNameG.foo).toEqual(fooG);
|
|
83
|
+
|
|
84
|
+
const fooL = literalArr.find((i) => i.name === "foo");
|
|
85
|
+
expect(byNameL.foo).toEqual(fooL);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("summarization works", () => {
|
|
89
|
+
const r = dictArr(...genericArr);
|
|
90
|
+
expect(r.sum("cost")).toBe(60);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("counting of non-required props works", () => {
|
|
94
|
+
const r = dictArr(...genericArr);
|
|
95
|
+
expect(r.count("color")).toBe(1);
|
|
96
|
+
expect(r.count("cost")).toBe(2);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("uniqueness", () => {
|
|
100
|
+
const r = dictArr(...genericArr);
|
|
101
|
+
const color = r.unique("color");
|
|
102
|
+
const title = r.unique("title");
|
|
103
|
+
|
|
104
|
+
expect(color.isUnique).toBeTruthy();
|
|
105
|
+
expect(color.includedUndefined).toBeTruthy();
|
|
106
|
+
expect(color.values).toEqual([undefined, "green"]);
|
|
107
|
+
expect(title.isUnique).toBeFalsy();
|
|
108
|
+
expect(title.values).toEqual(["one", "two"]);
|
|
109
|
+
});
|
|
110
|
+
});
|