typeshi 2.2.1 → 2.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1 @@
1
+ Copy/paste into your project if you find something useful.
@@ -5,4 +5,5 @@ export * from "./io";
5
5
  export * from "./regex";
6
6
  export * from "./argumentValidation";
7
7
  export * from "./typeValidation";
8
+ export * from "./utilityTypes";
8
9
  export * from "./object";
@@ -21,4 +21,5 @@ __exportStar(require("./io"), exports);
21
21
  __exportStar(require("./regex"), exports);
22
22
  __exportStar(require("./argumentValidation"), exports);
23
23
  __exportStar(require("./typeValidation"), exports);
24
+ __exportStar(require("./utilityTypes"), exports);
24
25
  __exportStar(require("./object"), exports);
@@ -100,3 +100,12 @@ export declare function hasDefinedEntry<T extends object>(obj: any, key: keyof T
100
100
  export declare function containsKey<T extends object, K extends (keyof T | (keyof any & {})) = any>(obj: T, ...keys: K[]): obj is {
101
101
  [K in keyof T]: T[K];
102
102
  };
103
+ /**
104
+ * @deprecated
105
+ * @param objA `Record<string, any>`
106
+ * @param objB `Record<string, any>`
107
+ * @returns **`areEquivalentObjects`** `boolean`
108
+ * - `true` `if` `objA` and `objB` are equivalent objects (same keys and values, including nested objects and arrays),
109
+ * - `false` `otherwise`.
110
+ */
111
+ export declare function areEquivalentObjects(objA: Record<string, any>, objB: Record<string, any>): boolean;
@@ -11,7 +11,9 @@ exports.picked = picked;
11
11
  exports.enforceMaxLength = enforceMaxLength;
12
12
  exports.hasDefinedEntry = hasDefinedEntry;
13
13
  exports.containsKey = containsKey;
14
+ exports.areEquivalentObjects = areEquivalentObjects;
14
15
  const typeValidation_1 = require("./typeValidation");
16
+ const index_1 = require("./regex/index");
15
17
  /**
16
18
  * @param obj `S` - source object (e.g., Request Body)
17
19
  * @param schema {@link TransformationSchema}`<T, S>` - map of keys to transformation functions.
@@ -177,3 +179,33 @@ function containsKey(obj, ...keys) {
177
179
  }
178
180
  return true;
179
181
  }
182
+ /**
183
+ * @deprecated
184
+ * @param objA `Record<string, any>`
185
+ * @param objB `Record<string, any>`
186
+ * @returns **`areEquivalentObjects`** `boolean`
187
+ * - `true` `if` `objA` and `objB` are equivalent objects (same keys and values, including nested objects and arrays),
188
+ * - `false` `otherwise`.
189
+ */
190
+ function areEquivalentObjects(objA, objB) {
191
+ if (!(0, typeValidation_1.isObject)(objA) || !(0, typeValidation_1.isObject)(objB))
192
+ return false;
193
+ const keysA = Object.keys(objA);
194
+ const keysB = Object.keys(objB);
195
+ if (keysA.length !== keysB.length)
196
+ return false;
197
+ return keysA.every(key => {
198
+ if (!containsKey(objB, key))
199
+ return false; // key not in both objects
200
+ const valA = objA[key];
201
+ const valB = objB[key];
202
+ if (Array.isArray(valA) && Array.isArray(valB)) {
203
+ return valA.length === valB.length
204
+ && valA.every((item) => valB.includes(item));
205
+ }
206
+ else if (typeof valA === "object" && valA && typeof valB === "object" && valB) {
207
+ return areEquivalentObjects(valA, valB);
208
+ }
209
+ return (0, index_1.equivalentAlphanumericStrings)(valA, valB);
210
+ });
211
+ }
@@ -1,6 +1,29 @@
1
- /** `re` = `/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/` */
1
+ export interface ParsedEmailAddress {
2
+ /** the whole email address */
3
+ address: string;
4
+ /** `localPart` the part before the @ sign */
5
+ local: string;
6
+ /** the part after the @ sign */
7
+ domain: string;
8
+ /** sender's display name if present in source string */
9
+ displayName?: string;
10
+ }
11
+ /**
12
+ * @param s `string`
13
+ * - e.g. `"{local}@{domain}"`, `"{displayName} <{local}@{domain}>"`
14
+ * @returns **`result`** {@link ParsedEmailAddress}` | null`
15
+ * @note `ParsedEmail.displayName` will be `undefined` if not present in `s`
16
+ * - prefix `/^no(-)?reply\s*(?=\w)/i` is removed from `displayName`
17
+ */
18
+ export declare function parseEmailAddress(s: string): ParsedEmailAddress | null;
19
+ /** @deprecated
20
+ * `re` = `/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/` */
2
21
  export declare const EMAIL_REGEX: RegExp;
3
- /**return true if matches {@link EMAIL_REGEX} and does not include pattern/string specified in `excludeSubstrings` */
4
- export declare function isValidEmail(email: string, excludeSubstrings?: string | RegExp | string[]): boolean;
5
- /** @returns **`email`**: `string` - the first email that matches {@link EMAIL_REGEX} or an empty string `''`*/
22
+ /**
23
+ * @deprecated
24
+ * return true if matches {@link EMAIL_REGEX} and does not include pattern/string specified in `excludeSubstrings` */
25
+ export declare function isValidEmail(email: string, excludeSubstrings?: string | string[] | RegExp): boolean;
26
+ /**
27
+ * @deprecated
28
+ * @returns `RegExpMatchArray` {@link EMAIL_REGEX} */
6
29
  export declare function extractEmail(email: string): RegExpMatchArray | null;
@@ -1,24 +1,55 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EMAIL_REGEX = void 0;
4
+ exports.parseEmailAddress = parseEmailAddress;
4
5
  exports.isValidEmail = isValidEmail;
5
6
  exports.extractEmail = extractEmail;
6
7
  /**
7
8
  * @file src/utils/regex/email.ts
8
9
  */
9
- const stringOperations_1 = require("./stringOperations");
10
- const StringOptions_1 = require("./types/StringOptions");
11
- /** `re` = `/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/` */
12
- exports.EMAIL_REGEX = new RegExp(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/, StringOptions_1.RegExpFlagsEnum.GLOBAL);
13
- /**return true if matches {@link EMAIL_REGEX} and does not include pattern/string specified in `excludeSubstrings` */
10
+ const Str_1 = require("./Str");
11
+ /**
12
+ * @param s `string`
13
+ * - e.g. `"{local}@{domain}"`, `"{displayName} <{local}@{domain}>"`
14
+ * @returns **`result`** {@link ParsedEmailAddress}` | null`
15
+ * @note `ParsedEmail.displayName` will be `undefined` if not present in `s`
16
+ * - prefix `/^no(-)?reply\s*(?=\w)/i` is removed from `displayName`
17
+ */
18
+ function parseEmailAddress(s) {
19
+ const emailRegex = /([a-zA-Z0-9._%+-]+)@([a-zA-Z0-9.-]+\.[a-zA-Z]{2,})/g;
20
+ const matchArrays = Array.from(s.matchAll(emailRegex));
21
+ if (matchArrays.length === 0)
22
+ return null;
23
+ const match = matchArrays[0];
24
+ const [address, local, domain] = match;
25
+ const displayName = (s
26
+ .slice(0, match.index ?? 0)
27
+ .trim()
28
+ .replace(/^no(-)?reply\s*(?=\w)/i, '')
29
+ .replace(/<$/, '')
30
+ .trim());
31
+ const result = { address, local, domain };
32
+ if (displayName)
33
+ result.displayName = displayName;
34
+ return result;
35
+ }
36
+ /** @deprecated
37
+ * `re` = `/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/` */
38
+ exports.EMAIL_REGEX = new RegExp(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/);
39
+ /**
40
+ * @deprecated
41
+ * return true if matches {@link EMAIL_REGEX} and does not include pattern/string specified in `excludeSubstrings` */
14
42
  function isValidEmail(email, excludeSubstrings) {
15
43
  if (!email)
16
44
  return false;
17
45
  email = email.trim();
18
- return exports.EMAIL_REGEX.test(email)
19
- && (excludeSubstrings ? !(0, stringOperations_1.stringContainsAnyOf)(email, excludeSubstrings) : true);
46
+ return (excludeSubstrings
47
+ ? exports.EMAIL_REGEX.test(email) && !Str_1.Str.contains(email, excludeSubstrings)
48
+ : exports.EMAIL_REGEX.test(email));
20
49
  }
21
- /** @returns **`email`**: `string` - the first email that matches {@link EMAIL_REGEX} or an empty string `''`*/
50
+ /**
51
+ * @deprecated
52
+ * @returns `RegExpMatchArray` {@link EMAIL_REGEX} */
22
53
  function extractEmail(email) {
23
54
  if (!email)
24
55
  return null;
@@ -40,6 +40,9 @@ export declare function stringContainsAnyOf(s: string, substrings: string | stri
40
40
  * - **`false`** `otherwise`.
41
41
  */
42
42
  export declare function equivalentAlphanumericStrings(s1: string, s2: string, tolerance?: number): boolean;
43
- /** for simple regular expressions...
44
- * so like not ones that have parentheses, pipes, or curly braced numbers */
43
+ /**
44
+ * @deprecated
45
+ * for simple regular expressions...
46
+ * so like not ones that have parentheses, pipes, or curly braced numbers
47
+ * */
45
48
  export declare function extractSource(regex: RegExp): string;
@@ -128,7 +128,7 @@ function stringContainsAnyOf(s, substrings, ...flags) {
128
128
  regex = new RegExp(substrings, flagString);
129
129
  }
130
130
  if (!regex) {
131
- config_1.typeshiLogger.warn('containsAnyOf() Invalid substrings type. returning false.', config_1.INDENT_LOG_LINE + `Expected string, array of strings, or RegExp, but received: ${typeof substrings}, ${substrings}`);
131
+ config_1.typeshiLogger.warn('[containsAnyOf()] Invalid substrings type. returning false.', config_1.INDENT_LOG_LINE + `Expected string, array of strings, or RegExp, but received: ${typeof substrings}, ${substrings}`);
132
132
  return false; // Invalid substrings type
133
133
  }
134
134
  return regex.test(s);
@@ -178,8 +178,11 @@ function equivalentAlphanumericStrings(s1, s2, tolerance = 0.90) {
178
178
  }
179
179
  return false;
180
180
  }
181
- /** for simple regular expressions...
182
- * so like not ones that have parentheses, pipes, or curly braced numbers */
181
+ /**
182
+ * @deprecated
183
+ * for simple regular expressions...
184
+ * so like not ones that have parentheses, pipes, or curly braced numbers
185
+ * */
183
186
  function extractSource(regex) {
184
187
  if (!regex)
185
188
  return '';
@@ -7,7 +7,7 @@
7
7
  * - **`true`** if `value` is an array and has at least one element,
8
8
  * - **`false`** otherwise.
9
9
  */
10
- export declare function isNonEmptyArray<T>(value: any): value is Array<T> & {
10
+ export declare function isNonEmptyArray<T>(value: unknown): value is Array<T> & {
11
11
  length: number;
12
12
  };
13
13
  /**
@@ -16,7 +16,7 @@ export declare function isNonEmptyArray<T>(value: any): value is Array<T> & {
16
16
  * - **`true`** if `value` is an array and has no elements,
17
17
  * - **`false`** `otherwise`
18
18
  */
19
- export declare function isEmptyArray<T>(value: any): value is Array<T> & {
19
+ export declare function isEmptyArray<T>(value: unknown): value is Array<T> & {
20
20
  length: 0;
21
21
  };
22
22
  /**
@@ -27,13 +27,12 @@ export declare function isEmptyArray<T>(value: any): value is Array<T> & {
27
27
  * - `if` `false` then `value` can be empty array
28
28
  * @returns **`isIntegerArray`** `boolean` = `value is number[] & { length: number }`
29
29
  */
30
- export declare function isIntegerArray(value: any, requireNonNegative?: boolean, requireNonEmpty?: boolean): value is number[] & {
30
+ export declare function isIntegerArray(value: unknown, requireNonNegative?: boolean, requireNonEmpty?: boolean): value is number[] & {
31
31
  length: number;
32
32
  };
33
33
  /**
34
- * @consideration add param to allow for empty strings?
35
34
  * @param value `any`
36
- * @param requireNonEmpty `boolean` `default = true`
35
+ * @param requireNonEmpty `boolean` `aka "requireNonEmptyArray"` `default = true`
37
36
  * - `if` `true` then `value` must be array with at least 1 element and every element `isNonEmptyString`
38
37
  * - `if` `false` then `value` can be empty array
39
38
  * @returns **`isStringArray`** `boolean` = `value is string[] & { length: number }`
@@ -41,40 +40,6 @@ export declare function isIntegerArray(value: any, requireNonNegative?: boolean,
41
40
  export declare function isStringArray(value: any, requireNonEmpty?: boolean): value is string[] & {
42
41
  length: number;
43
42
  };
44
- /**
45
- * `fka hasNonTrivialKeys`
46
- * @note **passing in an array will return `false`.**
47
- * @note a value is considered trivial if {@link isEmpty}`(value)` returns `true` and vice versa
48
- * @param obj `any` The object to check.
49
- * @param requireAll `boolean` - flag indicating whether all values must be nontrivial or not
50
- * @returns **`hasNonTrivialEntries`** `boolean`
51
- * - **`true`** `if` the `obj` has non-empty keys,
52
- * - **`false`** `otherwise`
53
- */
54
- export declare function hasNonTrivialEntries<T extends object>(obj: T, requireAll?: boolean): obj is T;
55
- /**
56
- * @note uses `key in obj` for each element of param `keys`
57
- * @param obj `T extends Object` the object to check
58
- * @param keys `Array<keyof T> | string[] | string` the list of keys that obj must have
59
- * @param requireAll `boolean` defaults to `true`
60
- * - `if` `true`, all keys must be present in the object;
61
- * - `if` `false`, at least one key must be present
62
- * @param restrictKeys `boolean` defaults to `false`
63
- * - `if` `true`, only the keys provided in the `keys` param are allowed in the object;
64
- * - `if` `false`, the object can keys not included in the `keys` param.
65
- * @returns **`hasKeys`** `boolean`
66
- * - **`true`** `if` `obj` is of type 'object' and has the required key(s),
67
- * - **`false`** `otherwise`
68
- */
69
- export declare function hasKeys<T extends object>(obj: T, keys: Array<keyof T> | string[] | string, requireAll?: boolean, restrictKeys?: boolean): boolean;
70
- /**
71
- * @param objA `Record<string, any>`
72
- * @param objB `Record<string, any>`
73
- * @returns **`areEquivalentObjects`** `boolean`
74
- * - `true` `if` `objA` and `objB` are equivalent objects (same keys and values, including nested objects and arrays),
75
- * - `false` `otherwise`.
76
- */
77
- export declare function areEquivalentObjects(objA: Record<string, any>, objB: Record<string, any>): boolean;
78
43
  /**
79
44
  * @param value `any`
80
45
  * @param requireInteger `boolean` `default = false`
@@ -243,30 +208,31 @@ export declare function isNull(value: unknown): value is null;
243
208
  */
244
209
  export declare function isUndefined(value: any): value is undefined;
245
210
  export declare function isUndefinedOrNull(value: unknown): value is undefined | null;
246
- export type NumberKeys<T, Required extends boolean = false> = {
247
- [K in keyof T]: Required extends true ? (T[K] extends number ? K : never) : (T[K] extends number | undefined ? K : never);
248
- }[keyof T][];
249
- export type ArrayKeys<T, Required extends boolean = false> = {
250
- [K in keyof T]: Required extends true ? (T[K] extends Array<any> ? K : never) : (T[K] extends Array<any> | undefined ? K : never);
251
- }[keyof T][];
252
- export type ArrayOfTypeKeys<T, U, Required extends boolean = false> = {
253
- [K in keyof T]: Required extends true ? (T[K] extends Array<U> ? K : never) : (T[K] extends Array<U> | undefined ? K : never);
254
- }[keyof T][];
255
- export type StringKeys<T, Required extends boolean = false> = {
256
- [K in keyof T]: Required extends true ? (T[K] extends string ? K : never) : (T[K] extends string | undefined ? K : never);
257
- }[keyof T][];
258
- export type PrimitiveKeys<T, Required extends boolean = false> = {
259
- [K in keyof T]: Required extends true ? (T[K] extends string | number | boolean | null ? K : never) : (T[K] extends string | number | boolean | null | undefined ? K : never);
260
- }[keyof T][];
261
- export type Primitive = string | number | boolean | null | undefined;
262
- /** Get the union of all values of `T` (like `valueof T`) */
263
- export type ValueOf<T> = T[keyof T];
264
211
  /**
265
- * Keys of `T` whose values extend a given type `U`
266
- * @template T - The object type
267
- * @template U - The type to check each `T[K]` against
268
- * @template Required - Whether the key is required (allows for `undefined` values if `false`) `default = false`
212
+ * @deprecated
213
+ * `fka hasNonTrivialKeys`
214
+ * @note **passing in an array will return `false`.**
215
+ * @note a value is considered trivial if {@link isEmpty}`(value)` returns `true` and vice versa
216
+ * @param obj `any` The object to check.
217
+ * @param requireAll `boolean` - flag indicating whether all values must be nontrivial or not
218
+ * @returns **`hasNonTrivialEntries`** `boolean`
219
+ * - **`true`** `if` the `obj` has non-empty keys,
220
+ * - **`false`** `otherwise`
269
221
  */
270
- export type KeysOfType<T, U, Required extends boolean = false> = {
271
- [K in keyof T]: Required extends true ? (T[K] extends U ? K : never) : (T[K] extends U | undefined ? K : never);
272
- }[keyof T][];
222
+ export declare function hasNonTrivialEntries<T extends object>(obj: T, requireAll?: boolean): obj is T;
223
+ /**
224
+ * @deprecated
225
+ * @note uses `key in obj` for each element of param `keys`
226
+ * @param obj `T extends Object` the object to check
227
+ * @param keys `Array<keyof T> | string[] | string` the list of keys that obj must have
228
+ * @param requireAll `boolean` defaults to `true`
229
+ * - `if` `true`, all keys must be present in the object;
230
+ * - `if` `false`, at least one key must be present
231
+ * @param restrictKeys `boolean` defaults to `false`
232
+ * - `if` `true`, only the keys provided in the `keys` param are allowed in the object;
233
+ * - `if` `false`, the object can keys not included in the `keys` param.
234
+ * @returns **`hasKeys`** `boolean`
235
+ * - **`true`** `if` `obj` is of type 'object' and has the required key(s),
236
+ * - **`false`** `otherwise`
237
+ */
238
+ export declare function hasKeys<T extends object>(obj: T, keys: Array<keyof T> | string[] | string, requireAll?: boolean, restrictKeys?: boolean): boolean;
@@ -8,9 +8,6 @@ exports.isNonEmptyArray = isNonEmptyArray;
8
8
  exports.isEmptyArray = isEmptyArray;
9
9
  exports.isIntegerArray = isIntegerArray;
10
10
  exports.isStringArray = isStringArray;
11
- exports.hasNonTrivialEntries = hasNonTrivialEntries;
12
- exports.hasKeys = hasKeys;
13
- exports.areEquivalentObjects = areEquivalentObjects;
14
11
  exports.isNumeric = isNumeric;
15
12
  exports.isNonEmptyString = isNonEmptyString;
16
13
  exports.isPrimitiveValue = isPrimitiveValue;
@@ -23,7 +20,8 @@ exports.isFunction = isFunction;
23
20
  exports.isNull = isNull;
24
21
  exports.isUndefined = isUndefined;
25
22
  exports.isUndefinedOrNull = isUndefinedOrNull;
26
- const index_1 = require("./regex/index");
23
+ exports.hasNonTrivialEntries = hasNonTrivialEntries;
24
+ exports.hasKeys = hasKeys;
27
25
  /**
28
26
  * @param value
29
27
  * @returns **`isNonEmptyArray`** `boolean` = `value is Array<T> & { length: number }`
@@ -55,10 +53,10 @@ function isIntegerArray(value, requireNonNegative = false, requireNonEmpty = tru
55
53
  ? isNonEmptyArray(value) && value.every(el => isInteger(el, requireNonNegative))
56
54
  : isEmptyArray(value));
57
55
  }
56
+ // * @consideration add param to allow for empty strings?
58
57
  /**
59
- * @consideration add param to allow for empty strings?
60
58
  * @param value `any`
61
- * @param requireNonEmpty `boolean` `default = true`
59
+ * @param requireNonEmpty `boolean` `aka "requireNonEmptyArray"` `default = true`
62
60
  * - `if` `true` then `value` must be array with at least 1 element and every element `isNonEmptyString`
63
61
  * - `if` `false` then `value` can be empty array
64
62
  * @returns **`isStringArray`** `boolean` = `value is string[] & { length: number }`
@@ -68,103 +66,6 @@ function isStringArray(value, requireNonEmpty = true) {
68
66
  ? isNonEmptyArray(value) && value.every(el => isNonEmptyString(el))
69
67
  : isEmptyArray(value));
70
68
  }
71
- // maybe deprecate this
72
- /**
73
- * `fka hasNonTrivialKeys`
74
- * @note **passing in an array will return `false`.**
75
- * @note a value is considered trivial if {@link isEmpty}`(value)` returns `true` and vice versa
76
- * @param obj `any` The object to check.
77
- * @param requireAll `boolean` - flag indicating whether all values must be nontrivial or not
78
- * @returns **`hasNonTrivialEntries`** `boolean`
79
- * - **`true`** `if` the `obj` has non-empty keys,
80
- * - **`false`** `otherwise`
81
- */
82
- function hasNonTrivialEntries(obj, requireAll = false) {
83
- if (!isObject(obj)) {
84
- return false;
85
- }
86
- return (requireAll
87
- ? Object.values(obj).every(v => !isEmpty(v))
88
- : Object.values(obj).some(v => !isEmpty(v)));
89
- }
90
- // @TODO add overload on param `keys` where keys = `{ required: string[], optional: string[] }`
91
- // maybe deprecate this
92
- /**
93
- * @note uses `key in obj` for each element of param `keys`
94
- * @param obj `T extends Object` the object to check
95
- * @param keys `Array<keyof T> | string[] | string` the list of keys that obj must have
96
- * @param requireAll `boolean` defaults to `true`
97
- * - `if` `true`, all keys must be present in the object;
98
- * - `if` `false`, at least one key must be present
99
- * @param restrictKeys `boolean` defaults to `false`
100
- * - `if` `true`, only the keys provided in the `keys` param are allowed in the object;
101
- * - `if` `false`, the object can keys not included in the `keys` param.
102
- * @returns **`hasKeys`** `boolean`
103
- * - **`true`** `if` `obj` is of type 'object' and has the required key(s),
104
- * - **`false`** `otherwise`
105
- */
106
- function hasKeys(obj, keys, requireAll = true, restrictKeys = false) {
107
- if (!obj || typeof obj !== 'object') {
108
- return false;
109
- }
110
- if (keys === null || keys === undefined) {
111
- return false;
112
- }
113
- if (!isNonEmptyArray(keys)) {
114
- keys = [keys]; // Convert string (assumed to be single key) to array of keys
115
- }
116
- let numKeysFound = 0;
117
- for (const key of keys) {
118
- if (key in obj) {
119
- numKeysFound++;
120
- if (!requireAll && !restrictKeys) {
121
- return true;
122
- }
123
- }
124
- else if (requireAll) { // and a key is not found
125
- return false;
126
- }
127
- }
128
- if (restrictKeys) {
129
- // If restrictKeys is true, check that no other keys are present in the object
130
- const objKeys = Object.keys(obj);
131
- const extraKeys = objKeys.filter(k => !keys.includes(k));
132
- if (extraKeys.length > 0) {
133
- return false; // Found keys not in the allowed list
134
- }
135
- }
136
- return requireAll ? numKeysFound === keys.length : numKeysFound > 0;
137
- }
138
- /**
139
- * @param objA `Record<string, any>`
140
- * @param objB `Record<string, any>`
141
- * @returns **`areEquivalentObjects`** `boolean`
142
- * - `true` `if` `objA` and `objB` are equivalent objects (same keys and values, including nested objects and arrays),
143
- * - `false` `otherwise`.
144
- */
145
- function areEquivalentObjects(objA, objB) {
146
- if (!objA || typeof objA !== 'object' || !objB || typeof objB !== 'object') {
147
- return false;
148
- }
149
- const keysA = Object.keys(objA);
150
- const keysB = Object.keys(objB);
151
- if (keysA.length !== keysB.length)
152
- return false;
153
- return keysA.every(key => {
154
- if (!hasKeys(objB, key))
155
- return false; // key not in both objects
156
- const valA = objA[key];
157
- const valB = objB[key];
158
- if (Array.isArray(valA) && Array.isArray(valB)) {
159
- return valA.length === valB.length
160
- && valA.every((item) => valB.includes(item));
161
- }
162
- else if (typeof valA === "object" && valA && typeof valB === "object" && valB) {
163
- return areEquivalentObjects(valA, valB);
164
- }
165
- return (0, index_1.equivalentAlphanumericStrings)(valA, valB);
166
- });
167
- }
168
69
  /**
169
70
  * @param value `any`
170
71
  * @param requireInteger `boolean` `default = false`
@@ -456,3 +357,70 @@ function isUndefined(value) {
456
357
  function isUndefinedOrNull(value) {
457
358
  return value === undefined || value === null;
458
359
  }
360
+ /**
361
+ * @deprecated
362
+ * `fka hasNonTrivialKeys`
363
+ * @note **passing in an array will return `false`.**
364
+ * @note a value is considered trivial if {@link isEmpty}`(value)` returns `true` and vice versa
365
+ * @param obj `any` The object to check.
366
+ * @param requireAll `boolean` - flag indicating whether all values must be nontrivial or not
367
+ * @returns **`hasNonTrivialEntries`** `boolean`
368
+ * - **`true`** `if` the `obj` has non-empty keys,
369
+ * - **`false`** `otherwise`
370
+ */
371
+ function hasNonTrivialEntries(obj, requireAll = false) {
372
+ if (!isObject(obj)) {
373
+ return false;
374
+ }
375
+ return (requireAll
376
+ ? Object.values(obj).every(v => !isEmpty(v))
377
+ : Object.values(obj).some(v => !isEmpty(v)));
378
+ }
379
+ // @TODO add overload on param `keys` where keys = `{ required: string[], optional: string[] }`
380
+ /**
381
+ * @deprecated
382
+ * @note uses `key in obj` for each element of param `keys`
383
+ * @param obj `T extends Object` the object to check
384
+ * @param keys `Array<keyof T> | string[] | string` the list of keys that obj must have
385
+ * @param requireAll `boolean` defaults to `true`
386
+ * - `if` `true`, all keys must be present in the object;
387
+ * - `if` `false`, at least one key must be present
388
+ * @param restrictKeys `boolean` defaults to `false`
389
+ * - `if` `true`, only the keys provided in the `keys` param are allowed in the object;
390
+ * - `if` `false`, the object can keys not included in the `keys` param.
391
+ * @returns **`hasKeys`** `boolean`
392
+ * - **`true`** `if` `obj` is of type 'object' and has the required key(s),
393
+ * - **`false`** `otherwise`
394
+ */
395
+ function hasKeys(obj, keys, requireAll = true, restrictKeys = false) {
396
+ if (!obj || typeof obj !== 'object') {
397
+ return false;
398
+ }
399
+ if (keys === null || keys === undefined) {
400
+ return false;
401
+ }
402
+ if (!isNonEmptyArray(keys)) {
403
+ keys = [keys]; // Convert string (assumed to be single key) to array of keys
404
+ }
405
+ let numKeysFound = 0;
406
+ for (const key of keys) {
407
+ if (key in obj) {
408
+ numKeysFound++;
409
+ if (!requireAll && !restrictKeys) {
410
+ return true;
411
+ }
412
+ }
413
+ else if (requireAll) { // and a key is not found
414
+ return false;
415
+ }
416
+ }
417
+ if (restrictKeys) {
418
+ // If restrictKeys is true, check that no other keys are present in the object
419
+ const objKeys = Object.keys(obj);
420
+ const extraKeys = objKeys.filter(k => !keys.includes(k));
421
+ if (extraKeys.length > 0) {
422
+ return false; // Found keys not in the allowed list
423
+ }
424
+ }
425
+ return requireAll ? numKeysFound === keys.length : numKeysFound > 0;
426
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @file src/utils/utilityTypes.ts
3
+ */
4
+ export type NumberKeys<T, Required extends boolean = false> = {
5
+ [K in keyof T]: Required extends true ? (T[K] extends number ? K : never) : (T[K] extends number | undefined ? K : never);
6
+ }[keyof T][];
7
+ export type ArrayKeys<T, Required extends boolean = false> = {
8
+ [K in keyof T]: Required extends true ? (T[K] extends Array<any> ? K : never) : (T[K] extends Array<any> | undefined ? K : never);
9
+ }[keyof T][];
10
+ export type ArrayOfTypeKeys<T, U, Required extends boolean = false> = {
11
+ [K in keyof T]: Required extends true ? (T[K] extends Array<U> ? K : never) : (T[K] extends Array<U> | undefined ? K : never);
12
+ }[keyof T][];
13
+ export type StringKeys<T, Required extends boolean = false> = {
14
+ [K in keyof T]: Required extends true ? (T[K] extends string ? K : never) : (T[K] extends string | undefined ? K : never);
15
+ }[keyof T][];
16
+ export type PrimitiveKeys<T, Required extends boolean = false> = {
17
+ [K in keyof T]: Required extends true ? (T[K] extends string | number | boolean | null ? K : never) : (T[K] extends string | number | boolean | null | undefined ? K : never);
18
+ }[keyof T][];
19
+ export type Primitive = string | number | boolean | null | undefined;
20
+ /** Get the union of all values of `T` (like `valueof T`) */
21
+ export type ValueOf<T> = T[keyof T];
22
+ /**
23
+ * Keys of `T` whose values extend a given type `U`
24
+ * @template T - The object type
25
+ * @template U - The type to check each `T[K]` against
26
+ * @template Required - Whether the key is required (allows for `undefined` values if `false`) `default = false`
27
+ */
28
+ export type KeysOfType<T, U, Required extends boolean = false> = {
29
+ [K in keyof T]: Required extends true ? (T[K] extends U ? K : never) : (T[K] extends U | undefined ? K : never);
30
+ }[keyof T][];
31
+ /**
32
+ * use for non-strict autocomplete of enum/literal value types
33
+ * @example
34
+ * JobPlatformEnum = { GREENHOUSE: 'GREENHOUSE', WORKDAY: 'WORKDAY' } as const;
35
+ * type JobPlatformEnum = (typeof JobPlatformEnum)[keyof typeof JobPlatformEnum]; // 'GREENHOUSE' | 'WORKDAY'
36
+ * let key: NonStrict<JobPlatformEnum>; // `key` allows any string, but will have the enum values as suggested auto-complete values
37
+ * key = 'hello'; // ok
38
+ * key = 9; // Error: Type '9' is not assignable to type 'NonStrict<JobPlatformEnum>'
39
+ * */
40
+ export type NonStrict<T extends string | number> = T | (T extends string ? (string & {}) : (number & {}));
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ /**
3
+ * @file src/utils/utilityTypes.ts
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "typeshi",
3
- "version": "2.2.1",
3
+ "version": "2.2.2",
4
4
  "description": "TypeScript utility modules",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",