@ezez/utils 1.0.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/.prettierignore +3 -0
- package/.prettierrc.json +1 -0
- package/CHANGELOG.md +244 -0
- package/LICENSE +21 -0
- package/README.md +79 -0
- package/babel.config.cjs +6 -0
- package/dist/cap.d.ts +3 -0
- package/dist/cap.d.ts.map +1 -0
- package/dist/cap.js +14 -0
- package/dist/cap.js.map +1 -0
- package/dist/capitalize.d.ts +3 -0
- package/dist/capitalize.d.ts.map +1 -0
- package/dist/capitalize.js +9 -0
- package/dist/capitalize.js.map +1 -0
- package/dist/coalesce.d.ts +3 -0
- package/dist/coalesce.d.ts.map +1 -0
- package/dist/coalesce.js +14 -0
- package/dist/coalesce.js.map +1 -0
- package/dist/ensureArray.d.ts +3 -0
- package/dist/ensureArray.d.ts.map +1 -0
- package/dist/ensureArray.js +11 -0
- package/dist/ensureArray.js.map +1 -0
- package/dist/ensureError.d.ts +3 -0
- package/dist/ensureError.d.ts.map +1 -0
- package/dist/ensureError.js +11 -0
- package/dist/ensureError.js.map +1 -0
- package/dist/get.d.ts +7 -0
- package/dist/get.d.ts.map +1 -0
- package/dist/get.js +19 -0
- package/dist/get.js.map +1 -0
- package/dist/getMultiple.d.ts +7 -0
- package/dist/getMultiple.d.ts.map +1 -0
- package/dist/getMultiple.js +18 -0
- package/dist/getMultiple.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +49 -0
- package/dist/index.js.map +1 -0
- package/dist/insertSeparator.d.ts +3 -0
- package/dist/insertSeparator.d.ts.map +1 -0
- package/dist/insertSeparator.js +18 -0
- package/dist/insertSeparator.js.map +1 -0
- package/dist/isEmpty.d.ts +3 -0
- package/dist/isEmpty.d.ts.map +1 -0
- package/dist/isEmpty.js +26 -0
- package/dist/isEmpty.js.map +1 -0
- package/dist/isPlainObject.d.ts +3 -0
- package/dist/isPlainObject.d.ts.map +1 -0
- package/dist/isPlainObject.js +10 -0
- package/dist/isPlainObject.js.map +1 -0
- package/dist/last.d.ts +3 -0
- package/dist/last.d.ts.map +1 -0
- package/dist/last.js +6 -0
- package/dist/last.js.map +1 -0
- package/dist/mapAsync.d.ts +3 -0
- package/dist/mapAsync.d.ts.map +1 -0
- package/dist/mapAsync.js +22 -0
- package/dist/mapAsync.js.map +1 -0
- package/dist/mapValues.d.ts +6 -0
- package/dist/mapValues.d.ts.map +1 -0
- package/dist/mapValues.js +18 -0
- package/dist/mapValues.js.map +1 -0
- package/dist/match.d.ts +8 -0
- package/dist/match.d.ts.map +1 -0
- package/dist/match.js +20 -0
- package/dist/match.js.map +1 -0
- package/dist/merge.d.ts +15 -0
- package/dist/merge.d.ts.map +1 -0
- package/dist/merge.js +28 -0
- package/dist/merge.js.map +1 -0
- package/dist/mostFrequent.d.ts +3 -0
- package/dist/mostFrequent.d.ts.map +1 -0
- package/dist/mostFrequent.js +21 -0
- package/dist/mostFrequent.js.map +1 -0
- package/dist/noop.d.ts +3 -0
- package/dist/noop.d.ts.map +1 -0
- package/dist/noop.js +6 -0
- package/dist/noop.js.map +1 -0
- package/dist/omit.d.ts +5 -0
- package/dist/omit.d.ts.map +1 -0
- package/dist/omit.js +21 -0
- package/dist/omit.js.map +1 -0
- package/dist/package.json +1 -0
- package/dist/pick.d.ts +5 -0
- package/dist/pick.d.ts.map +1 -0
- package/dist/pick.js +20 -0
- package/dist/pick.js.map +1 -0
- package/dist/pull.d.ts +3 -0
- package/dist/pull.d.ts.map +1 -0
- package/dist/pull.js +14 -0
- package/dist/pull.js.map +1 -0
- package/dist/remove.d.ts +3 -0
- package/dist/remove.d.ts.map +1 -0
- package/dist/remove.js +18 -0
- package/dist/remove.js.map +1 -0
- package/dist/rethrow.d.ts +3 -0
- package/dist/rethrow.d.ts.map +1 -0
- package/dist/rethrow.js +8 -0
- package/dist/rethrow.js.map +1 -0
- package/dist/scale.d.ts +3 -0
- package/dist/scale.d.ts.map +1 -0
- package/dist/scale.js +8 -0
- package/dist/scale.js.map +1 -0
- package/dist/seq.d.ts +8 -0
- package/dist/seq.d.ts.map +1 -0
- package/dist/seq.js +45 -0
- package/dist/seq.js.map +1 -0
- package/dist/set.d.ts +7 -0
- package/dist/set.d.ts.map +1 -0
- package/dist/set.js +25 -0
- package/dist/set.js.map +1 -0
- package/dist/setImmutable.d.ts +8 -0
- package/dist/setImmutable.d.ts.map +1 -0
- package/dist/setImmutable.js +59 -0
- package/dist/setImmutable.js.map +1 -0
- package/dist/sortBy.d.ts +3 -0
- package/dist/sortBy.d.ts.map +1 -0
- package/dist/sortBy.js +15 -0
- package/dist/sortBy.js.map +1 -0
- package/dist/throttle.d.ts +13 -0
- package/dist/throttle.d.ts.map +1 -0
- package/dist/throttle.js +82 -0
- package/dist/throttle.js.map +1 -0
- package/dist/truthy.d.ts +3 -0
- package/dist/truthy.d.ts.map +1 -0
- package/dist/truthy.js +8 -0
- package/dist/truthy.js.map +1 -0
- package/dist/wait.d.ts +3 -0
- package/dist/wait.d.ts.map +1 -0
- package/dist/wait.js +10 -0
- package/dist/wait.js.map +1 -0
- package/dist/waitFor.d.ts +3 -0
- package/dist/waitFor.d.ts.map +1 -0
- package/dist/waitFor.js +34 -0
- package/dist/waitFor.js.map +1 -0
- package/dist/waitSync.d.ts +3 -0
- package/dist/waitSync.d.ts.map +1 -0
- package/dist/waitSync.js +9 -0
- package/dist/waitSync.js.map +1 -0
- package/docs/.nojekyll +1 -0
- package/docs/assets/highlight.css +78 -0
- package/docs/assets/main.js +58 -0
- package/docs/assets/pages.css +14 -0
- package/docs/assets/search.js +1 -0
- package/docs/assets/style.css +1280 -0
- package/docs/functions/cap.html +116 -0
- package/docs/functions/capitalize.html +121 -0
- package/docs/functions/coalesce.html +127 -0
- package/docs/functions/ensureArray.html +114 -0
- package/docs/functions/ensureError.html +110 -0
- package/docs/functions/get.html +138 -0
- package/docs/functions/getMultiple.html +129 -0
- package/docs/functions/insertSeparator.html +123 -0
- package/docs/functions/isEmpty.html +139 -0
- package/docs/functions/isPlainObject.html +108 -0
- package/docs/functions/last.html +126 -0
- package/docs/functions/mapAsync.html +144 -0
- package/docs/functions/mapValues.html +130 -0
- package/docs/functions/match.html +122 -0
- package/docs/functions/merge.html +464 -0
- package/docs/functions/mostFrequent.html +111 -0
- package/docs/functions/noop.html +101 -0
- package/docs/functions/omit.html +129 -0
- package/docs/functions/pick.html +129 -0
- package/docs/functions/pull.html +113 -0
- package/docs/functions/remove.html +129 -0
- package/docs/functions/rethrow.html +106 -0
- package/docs/functions/scale.html +117 -0
- package/docs/functions/seq.html +128 -0
- package/docs/functions/seqEarlyBreak.html +126 -0
- package/docs/functions/set.html +138 -0
- package/docs/functions/setImmutable.html +137 -0
- package/docs/functions/sortBy.html +135 -0
- package/docs/functions/throttle.html +131 -0
- package/docs/functions/truthy.html +118 -0
- package/docs/functions/wait.html +109 -0
- package/docs/functions/waitFor.html +130 -0
- package/docs/functions/waitSync.html +109 -0
- package/docs/index.html +182 -0
- package/docs/interfaces/GetMultipleSource.html +106 -0
- package/docs/interfaces/GetSource.html +106 -0
- package/docs/interfaces/SetImmutableSource.html +106 -0
- package/docs/interfaces/SetSource.html +106 -0
- package/docs/interfaces/ThrottleOptions.html +80 -0
- package/docs/interfaces/ThrottledFunctionExtras.html +96 -0
- package/docs/modules.html +152 -0
- package/docs/pages/Introduction.html +94 -0
- package/docs/types/MapValuesFn.html +134 -0
- package/docs/types/MatchCallback.html +113 -0
- package/docs/types/SeqEarlyBreaker.html +114 -0
- package/docs/types/SeqFn.html +112 -0
- package/docs/types/SeqFunctions.html +105 -0
- package/docs/types/SetImmutablePath.html +99 -0
- package/docs/types/ThrottledFunction.html +113 -0
- package/docs/variables/mapValuesUNSET.html +101 -0
- package/docs/variables/mergeUNSET.html +103 -0
- package/esm/cap.d.ts +3 -0
- package/esm/cap.d.ts.map +1 -0
- package/esm/cap.js +11 -0
- package/esm/cap.js.map +1 -0
- package/esm/capitalize.d.ts +3 -0
- package/esm/capitalize.d.ts.map +1 -0
- package/esm/capitalize.js +6 -0
- package/esm/capitalize.js.map +1 -0
- package/esm/coalesce.d.ts +3 -0
- package/esm/coalesce.d.ts.map +1 -0
- package/esm/coalesce.js +11 -0
- package/esm/coalesce.js.map +1 -0
- package/esm/ensureArray.d.ts +3 -0
- package/esm/ensureArray.d.ts.map +1 -0
- package/esm/ensureArray.js +8 -0
- package/esm/ensureArray.js.map +1 -0
- package/esm/ensureError.d.ts +3 -0
- package/esm/ensureError.d.ts.map +1 -0
- package/esm/ensureError.js +8 -0
- package/esm/ensureError.js.map +1 -0
- package/esm/get.d.ts +7 -0
- package/esm/get.d.ts.map +1 -0
- package/esm/get.js +16 -0
- package/esm/get.js.map +1 -0
- package/esm/getMultiple.d.ts +7 -0
- package/esm/getMultiple.d.ts.map +1 -0
- package/esm/getMultiple.js +15 -0
- package/esm/getMultiple.js.map +1 -0
- package/esm/index.d.ts +33 -0
- package/esm/index.d.ts.map +1 -0
- package/esm/index.js +33 -0
- package/esm/index.js.map +1 -0
- package/esm/insertSeparator.d.ts +3 -0
- package/esm/insertSeparator.d.ts.map +1 -0
- package/esm/insertSeparator.js +15 -0
- package/esm/insertSeparator.js.map +1 -0
- package/esm/isEmpty.d.ts +3 -0
- package/esm/isEmpty.d.ts.map +1 -0
- package/esm/isEmpty.js +23 -0
- package/esm/isEmpty.js.map +1 -0
- package/esm/isPlainObject.d.ts +3 -0
- package/esm/isPlainObject.d.ts.map +1 -0
- package/esm/isPlainObject.js +7 -0
- package/esm/isPlainObject.js.map +1 -0
- package/esm/last.d.ts +3 -0
- package/esm/last.d.ts.map +1 -0
- package/esm/last.js +3 -0
- package/esm/last.js.map +1 -0
- package/esm/mapAsync.d.ts +3 -0
- package/esm/mapAsync.d.ts.map +1 -0
- package/esm/mapAsync.js +19 -0
- package/esm/mapAsync.js.map +1 -0
- package/esm/mapValues.d.ts +6 -0
- package/esm/mapValues.d.ts.map +1 -0
- package/esm/mapValues.js +14 -0
- package/esm/mapValues.js.map +1 -0
- package/esm/match.d.ts +8 -0
- package/esm/match.d.ts.map +1 -0
- package/esm/match.js +17 -0
- package/esm/match.js.map +1 -0
- package/esm/merge.d.ts +15 -0
- package/esm/merge.d.ts.map +1 -0
- package/esm/merge.js +24 -0
- package/esm/merge.js.map +1 -0
- package/esm/mostFrequent.d.ts +3 -0
- package/esm/mostFrequent.d.ts.map +1 -0
- package/esm/mostFrequent.js +18 -0
- package/esm/mostFrequent.js.map +1 -0
- package/esm/noop.d.ts +3 -0
- package/esm/noop.d.ts.map +1 -0
- package/esm/noop.js +3 -0
- package/esm/noop.js.map +1 -0
- package/esm/omit.d.ts +5 -0
- package/esm/omit.d.ts.map +1 -0
- package/esm/omit.js +20 -0
- package/esm/omit.js.map +1 -0
- package/esm/pick.d.ts +5 -0
- package/esm/pick.d.ts.map +1 -0
- package/esm/pick.js +17 -0
- package/esm/pick.js.map +1 -0
- package/esm/pull.d.ts +3 -0
- package/esm/pull.d.ts.map +1 -0
- package/esm/pull.js +11 -0
- package/esm/pull.js.map +1 -0
- package/esm/remove.d.ts +3 -0
- package/esm/remove.d.ts.map +1 -0
- package/esm/remove.js +15 -0
- package/esm/remove.js.map +1 -0
- package/esm/rethrow.d.ts +3 -0
- package/esm/rethrow.d.ts.map +1 -0
- package/esm/rethrow.js +5 -0
- package/esm/rethrow.js.map +1 -0
- package/esm/scale.d.ts +3 -0
- package/esm/scale.d.ts.map +1 -0
- package/esm/scale.js +5 -0
- package/esm/scale.js.map +1 -0
- package/esm/seq.d.ts +8 -0
- package/esm/seq.d.ts.map +1 -0
- package/esm/seq.js +41 -0
- package/esm/seq.js.map +1 -0
- package/esm/set.d.ts +7 -0
- package/esm/set.d.ts.map +1 -0
- package/esm/set.js +22 -0
- package/esm/set.js.map +1 -0
- package/esm/setImmutable.d.ts +8 -0
- package/esm/setImmutable.d.ts.map +1 -0
- package/esm/setImmutable.js +56 -0
- package/esm/setImmutable.js.map +1 -0
- package/esm/sortBy.d.ts +3 -0
- package/esm/sortBy.d.ts.map +1 -0
- package/esm/sortBy.js +12 -0
- package/esm/sortBy.js.map +1 -0
- package/esm/throttle.d.ts +13 -0
- package/esm/throttle.d.ts.map +1 -0
- package/esm/throttle.js +79 -0
- package/esm/throttle.js.map +1 -0
- package/esm/truthy.d.ts +3 -0
- package/esm/truthy.d.ts.map +1 -0
- package/esm/truthy.js +5 -0
- package/esm/truthy.js.map +1 -0
- package/esm/wait.d.ts +3 -0
- package/esm/wait.d.ts.map +1 -0
- package/esm/wait.js +7 -0
- package/esm/wait.js.map +1 -0
- package/esm/waitFor.d.ts +3 -0
- package/esm/waitFor.d.ts.map +1 -0
- package/esm/waitFor.js +31 -0
- package/esm/waitFor.js.map +1 -0
- package/esm/waitSync.d.ts +3 -0
- package/esm/waitSync.d.ts.map +1 -0
- package/esm/waitSync.js +6 -0
- package/esm/waitSync.js.map +1 -0
- package/package.json +75 -0
- package/src/cap.spec.ts +36 -0
- package/src/cap.ts +19 -0
- package/src/capitalize.spec.ts +18 -0
- package/src/capitalize.ts +16 -0
- package/src/coalesce.spec.ts +23 -0
- package/src/coalesce.ts +21 -0
- package/src/ensureArray.spec.ts +87 -0
- package/src/ensureArray.ts +13 -0
- package/src/ensureError.spec.ts +29 -0
- package/src/ensureError.ts +15 -0
- package/src/get.spec.ts +183 -0
- package/src/get.ts +53 -0
- package/src/getMultiple.spec.ts +25 -0
- package/src/getMultiple.ts +47 -0
- package/src/index.ts +33 -0
- package/src/insertSeparator.spec.ts +29 -0
- package/src/insertSeparator.ts +22 -0
- package/src/isEmpty.spec.ts +130 -0
- package/src/isEmpty.ts +50 -0
- package/src/isPlainObject.spec.ts +42 -0
- package/src/isPlainObject.ts +18 -0
- package/src/last.spec.ts +88 -0
- package/src/last.ts +12 -0
- package/src/mapAsync.spec.ts +39 -0
- package/src/mapAsync.ts +41 -0
- package/src/mapValues.spec.ts +178 -0
- package/src/mapValues.ts +57 -0
- package/src/match.spec.ts +11 -0
- package/src/match.ts +30 -0
- package/src/merge.spec.ts +69 -0
- package/src/merge.ts +58 -0
- package/src/mostFrequent.spec.ts +35 -0
- package/src/mostFrequent.ts +27 -0
- package/src/noop.ts +8 -0
- package/src/omit.spec.ts +181 -0
- package/src/omit.ts +43 -0
- package/src/pick.spec.ts +168 -0
- package/src/pick.ts +39 -0
- package/src/pull.spec.ts +54 -0
- package/src/pull.ts +18 -0
- package/src/remove.spec.ts +63 -0
- package/src/remove.ts +26 -0
- package/src/rethrow.ts +12 -0
- package/src/scale.ts +18 -0
- package/src/seq.spec.ts +157 -0
- package/src/seq.ts +104 -0
- package/src/set.spec.ts +348 -0
- package/src/set.ts +59 -0
- package/src/setImmutable.spec.ts +241 -0
- package/src/setImmutable.ts +104 -0
- package/src/sortBy.spec.ts +56 -0
- package/src/sortBy.ts +24 -0
- package/src/throttle.spec.ts +136 -0
- package/src/throttle.ts +153 -0
- package/src/truthy.spec.ts +21 -0
- package/src/truthy.ts +13 -0
- package/src/wait.spec.ts +21 -0
- package/src/wait.ts +14 -0
- package/src/waitFor.ts +47 -0
- package/src/waitSync.spec.ts +21 -0
- package/src/waitSync.ts +14 -0
- package/tutorials/Introduction.md +1 -0
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensures given value is an instance of Error.
|
|
3
|
+
* @param {*} e - value to check
|
|
4
|
+
* @returns Error - original error or new Error instance
|
|
5
|
+
*/
|
|
6
|
+
const ensureError = (e: unknown) => {
|
|
7
|
+
if (e instanceof Error) {
|
|
8
|
+
return e;
|
|
9
|
+
}
|
|
10
|
+
return new Error("Expected error instance, got something else: " + String(e));
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
ensureError,
|
|
15
|
+
};
|
package/src/get.spec.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { get as _get } from "lodash";
|
|
2
|
+
|
|
3
|
+
import { get } from "./get.js";
|
|
4
|
+
|
|
5
|
+
const otherObject = { a: 5 };
|
|
6
|
+
const testObject = {
|
|
7
|
+
"product": {
|
|
8
|
+
name: "abc",
|
|
9
|
+
price: 123,
|
|
10
|
+
value: undefined,
|
|
11
|
+
empty: null,
|
|
12
|
+
notANumber: NaN,
|
|
13
|
+
object: otherObject,
|
|
14
|
+
},
|
|
15
|
+
"product[price]": 400,
|
|
16
|
+
"name": "string",
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const Test = function() {};
|
|
20
|
+
Test.prototype.nonOwn = 1;
|
|
21
|
+
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
23
|
+
const testInstance = new Test();
|
|
24
|
+
testInstance.own = 1;
|
|
25
|
+
Object.defineProperty(testInstance, "nonEnum", {
|
|
26
|
+
enumerable: false,
|
|
27
|
+
value: 15,
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("get", () => {
|
|
31
|
+
it("returns shallow property of an object, behaves like lodash", () => {
|
|
32
|
+
const value = get(testObject, "name");
|
|
33
|
+
value.must.equal("string");
|
|
34
|
+
|
|
35
|
+
const _value = _get(testObject, "name");
|
|
36
|
+
value.must.equal(_value);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("returns deep property of an object using array, behaves like lodash", () => {
|
|
40
|
+
const value = get(testObject, ["product", "name"]);
|
|
41
|
+
value.must.equal("abc");
|
|
42
|
+
|
|
43
|
+
const _value = _get(testObject, ["product", "name"]);
|
|
44
|
+
value.must.equal(_value);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("returns deep property of an object using dot-string, behaves like lodash", () => {
|
|
48
|
+
const value = get(testObject, "product.name");
|
|
49
|
+
value.must.equal("abc");
|
|
50
|
+
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
52
|
+
const _value = _get(testObject, "product.name");
|
|
53
|
+
value.must.equal(_value);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it("doesn't parse string as js code, behaves NOT like lodash", () => {
|
|
57
|
+
// Lodash intention-guessing example, inconsistency detected
|
|
58
|
+
const name = get(testObject, "product[name]");
|
|
59
|
+
(name === undefined).must.be.true();
|
|
60
|
+
|
|
61
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
62
|
+
const _name = _get(testObject, "product[name]"); // deep value
|
|
63
|
+
_name.must.equal("abc");
|
|
64
|
+
|
|
65
|
+
const price = get(testObject, "product[price]");
|
|
66
|
+
price.must.be.equal(400);
|
|
67
|
+
|
|
68
|
+
const _price = _get(testObject, "product[price]"); // shallow value
|
|
69
|
+
_price.must.equal(400);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it("returns undefined when value is not found and default isn't provided, behaves like lodash", () => {
|
|
73
|
+
const value = get(testObject, "product.something");
|
|
74
|
+
(value === undefined).must.be.true();
|
|
75
|
+
|
|
76
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
77
|
+
const _value = _get(testObject, "product.something");
|
|
78
|
+
(_value === undefined).must.be.true();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it("returns default value when property is missing, behaves like lodash", () => {
|
|
82
|
+
const value = get(testObject, "product.something", 3);
|
|
83
|
+
value.must.equal(3);
|
|
84
|
+
|
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
86
|
+
const _value = _get(testObject, "product.something", 3);
|
|
87
|
+
_value.must.equal(3);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("returns undefined when found value is explicitly undefined, behaves NOT like lodash", () => {
|
|
91
|
+
const value = get(testObject, "product.value", 3);
|
|
92
|
+
(value === undefined).must.be.true();
|
|
93
|
+
|
|
94
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
95
|
+
const _value = _get(testObject, "product.value", 3);
|
|
96
|
+
_value.must.equal(3);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("returns null when found value is explicitly null, behaves like lodash", () => {
|
|
100
|
+
const value = get(testObject, "product.empty", 3);
|
|
101
|
+
(value === null).must.be.true();
|
|
102
|
+
|
|
103
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
104
|
+
const _value = _get(testObject, "product.empty", 3);
|
|
105
|
+
(_value === null).must.be.true();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("returns NaN when found value is NaN, behaves like lodash", () => {
|
|
109
|
+
const value = get(testObject, "product.notANumber", 3);
|
|
110
|
+
value.must.be.nan();
|
|
111
|
+
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
113
|
+
const _value = _get(testObject, "product.notANumber", 3);
|
|
114
|
+
_value.must.be.nan();
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it("returns objects, behaves like lodash", () => {
|
|
118
|
+
const value = get(testObject, "product.object");
|
|
119
|
+
value.must.equal(otherObject);
|
|
120
|
+
|
|
121
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
122
|
+
const _value = _get(testObject, "product.object");
|
|
123
|
+
_value.must.equal(otherObject);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("works on nulls, behaves like lodash", () => {
|
|
127
|
+
const value = get(null, "product.object", 3);
|
|
128
|
+
value.must.equal(3);
|
|
129
|
+
|
|
130
|
+
const _value = _get(null, "product.object", 3);
|
|
131
|
+
_value.must.equal(3);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it("works on undefined, behaves like lodash", () => {
|
|
135
|
+
const value = get(undefined, "product.object", 3);
|
|
136
|
+
value.must.equal(3);
|
|
137
|
+
|
|
138
|
+
const _value = _get(undefined, "product.object", 3);
|
|
139
|
+
_value.must.equal(3);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("works on NaN, behaves like lodash", () => {
|
|
143
|
+
const value = get(NaN, "product.object", 3);
|
|
144
|
+
value.must.equal(3);
|
|
145
|
+
|
|
146
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
147
|
+
const _value = _get(NaN, "product.object", 3);
|
|
148
|
+
_value.must.equal(3);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it("returns inherited properties value, behaves like lodash", () => {
|
|
152
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
153
|
+
const value = get(testInstance, "nonOwn", 3);
|
|
154
|
+
value.must.equal(1);
|
|
155
|
+
|
|
156
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
157
|
+
const _value = _get(testInstance, "nonOwn", 3);
|
|
158
|
+
_value.must.equal(1);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it("returns non-enumerable properties value, behaves like lodash", () => {
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
163
|
+
const value = get(testInstance, "nonEnum", 3);
|
|
164
|
+
value.must.equal(15);
|
|
165
|
+
|
|
166
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
167
|
+
const _value = _get(testInstance, "nonEnum", 3);
|
|
168
|
+
_value.must.equal(15);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it("doesn't parse mixed array/dot-strings property selectors, behaves like lodash", () => {
|
|
172
|
+
const value = get(testObject, ["product", "object.a"], "def");
|
|
173
|
+
const value2 = get(testObject, "product.object.a", "def");
|
|
174
|
+
value.must.equal("def");
|
|
175
|
+
value2.must.equal(5);
|
|
176
|
+
|
|
177
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
178
|
+
const _value = _get(testObject, ["product", "object.a"], "def");
|
|
179
|
+
_value.must.equal("def");
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// @todo test Maps, Sets etc
|
|
183
|
+
});
|
package/src/get.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Source object to search in.
|
|
3
|
+
*
|
|
4
|
+
* @see {@link get}.
|
|
5
|
+
*/
|
|
6
|
+
interface Source { [key: string]: unknown }
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Returns the value at given path of given object. If path is not found then default value is returned. No exceptions
|
|
10
|
+
* are thrown when undefined/null value gets in the way.
|
|
11
|
+
*
|
|
12
|
+
* This is still too dynamic in nature to get full TypeScript support. Properties are not typed, return type is unknown.
|
|
13
|
+
* If your data access is statically known there is no need to use this function, just use `object.property` syntax with
|
|
14
|
+
* optional chaining.
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} source - source object to search in
|
|
17
|
+
* @param {string|Array<string>} property - path to the expected value written as dot-separated property names or array
|
|
18
|
+
* with property names. Use Array when your keys includes dots. Keys are treated literally, no parsing is done on keys.
|
|
19
|
+
* @param {*} [defaultValue] - value to return if path is not found. If path is found, but the value is undefined -
|
|
20
|
+
* default value will NOT be used, use `get(...) || default` instead
|
|
21
|
+
* @example get(object, "deep.property") // equals to safe `object.deep.property`
|
|
22
|
+
* @example get(object, ["deep", "property"]) // same as above
|
|
23
|
+
* @example get(object, "deep[0].property")
|
|
24
|
+
* // equals to:
|
|
25
|
+
* object["deep[0]"].property
|
|
26
|
+
* // not:
|
|
27
|
+
* object.deep[0].property
|
|
28
|
+
* @example get(object, "very.deep.property", 5)
|
|
29
|
+
* // if object.very.deep has also `property` property then it value will be returned, even if undefined
|
|
30
|
+
* // else `5` will be returned
|
|
31
|
+
* @returns {*} - found value or default value
|
|
32
|
+
*/
|
|
33
|
+
const get = (source: Source, property: string | string[], defaultValue: unknown = undefined): unknown => {
|
|
34
|
+
const properties = typeof property === "string" ? property.split(".") : [...property];
|
|
35
|
+
|
|
36
|
+
let result: unknown = source;
|
|
37
|
+
while (properties.length) {
|
|
38
|
+
const current = properties.shift()!;
|
|
39
|
+
if (result && typeof result === "object" && current in result) {
|
|
40
|
+
result = (result as Source)[current];
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
return defaultValue;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export { get };
|
|
50
|
+
|
|
51
|
+
export type {
|
|
52
|
+
Source as GetSource,
|
|
53
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { getMultiple } from "./getMultiple.js";
|
|
2
|
+
|
|
3
|
+
const testObject = {
|
|
4
|
+
product: {
|
|
5
|
+
title: "abc",
|
|
6
|
+
nothing: undefined,
|
|
7
|
+
},
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
describe("getMultiple", () => {
|
|
11
|
+
it("returns first property that exist, with mixed selectors", () => {
|
|
12
|
+
const value = getMultiple(testObject, 123, ["product", "xxx"], "product.title");
|
|
13
|
+
value.must.equal("abc");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("returns first property that exist even if value is undefined", () => {
|
|
17
|
+
const value = getMultiple(testObject, 123, ["product", "nothing"], "product.title");
|
|
18
|
+
(value === undefined).must.be.true();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("returns default value when nothing exists", () => {
|
|
22
|
+
const value = getMultiple(testObject, 123, ["product", "name"], "product.price");
|
|
23
|
+
value.must.equal(123);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { get } from "./get.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Source object to search in.
|
|
5
|
+
*
|
|
6
|
+
* @see {@link getMultiple}.
|
|
7
|
+
*/
|
|
8
|
+
interface Source { [key: string]: unknown }
|
|
9
|
+
|
|
10
|
+
const DEFAULT = {};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Returns first found value at given list of paths of given object. Will return and stop at undefined if found! If
|
|
14
|
+
* nothing is found then default value (required to pass) will be returned.
|
|
15
|
+
*
|
|
16
|
+
* This is still too dynamic in nature to get full TypeScript support. Properties are not typed, return type is unknown.
|
|
17
|
+
* If your data access is statically known there is no need to use this function, just use `object.property` syntax with
|
|
18
|
+
* optional chaining.
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} source - source object to search in
|
|
21
|
+
* @param {*} defaultValue - default value to return if nothing is found
|
|
22
|
+
* @param {...string|Array.<string>} paths - paths defined as dot-separated properties names or array of properties name
|
|
23
|
+
* @see {@link get} - for base usage example with single path only
|
|
24
|
+
* @example getMultiple(obj, 5, "details.error.message", ["error", "message"], "errorMessage")
|
|
25
|
+
* // will look for obj.details.error.message - if path does not exist
|
|
26
|
+
* // will look for obj.error.message - if not defined
|
|
27
|
+
* // will look for obj.errorMessage - if not defined
|
|
28
|
+
* // will return 5
|
|
29
|
+
* @returns {*} - found value or default value
|
|
30
|
+
*/
|
|
31
|
+
const getMultiple = (source: Source, defaultValue: unknown, ...paths: (string | string[])[]): unknown => {
|
|
32
|
+
const length = paths.length;
|
|
33
|
+
for (let i = 0; i < length; i++) {
|
|
34
|
+
const properties = paths[i];
|
|
35
|
+
const result = get(source, properties, DEFAULT);
|
|
36
|
+
if (result !== DEFAULT) {
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return defaultValue;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export { getMultiple };
|
|
44
|
+
|
|
45
|
+
export type {
|
|
46
|
+
Source as GetMultipleSource,
|
|
47
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/* eslint-disable import/max-dependencies */
|
|
2
|
+
export * from "./cap.js";
|
|
3
|
+
export * from "./capitalize.js";
|
|
4
|
+
export * from "./coalesce.js";
|
|
5
|
+
export * from "./ensureArray.js";
|
|
6
|
+
export * from "./ensureError.js";
|
|
7
|
+
export * from "./get.js";
|
|
8
|
+
export * from "./getMultiple.js";
|
|
9
|
+
export * from "./insertSeparator.js";
|
|
10
|
+
export * from "./isEmpty.js";
|
|
11
|
+
export * from "./isPlainObject.js";
|
|
12
|
+
export * from "./last.js";
|
|
13
|
+
export * from "./mapAsync.js";
|
|
14
|
+
export * from "./mapValues.js";
|
|
15
|
+
export * from "./match.js";
|
|
16
|
+
export * from "./merge.js";
|
|
17
|
+
export * from "./mostFrequent.js";
|
|
18
|
+
export * from "./noop.js";
|
|
19
|
+
export * from "./omit.js";
|
|
20
|
+
export * from "./pick.js";
|
|
21
|
+
export * from "./pull.js";
|
|
22
|
+
export * from "./remove.js";
|
|
23
|
+
export * from "./rethrow.js";
|
|
24
|
+
export * from "./scale.js";
|
|
25
|
+
export * from "./seq.js";
|
|
26
|
+
export * from "./set.js";
|
|
27
|
+
export * from "./setImmutable.js";
|
|
28
|
+
export * from "./sortBy.js";
|
|
29
|
+
export * from "./throttle.js";
|
|
30
|
+
export * from "./truthy.js";
|
|
31
|
+
export * from "./wait.js";
|
|
32
|
+
export * from "./waitFor.js";
|
|
33
|
+
export * from "./waitSync.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { insertSeparator } from "./insertSeparator.js";
|
|
2
|
+
|
|
3
|
+
describe("insertSeparator", () => {
|
|
4
|
+
it("inserts value on array with data", () => {
|
|
5
|
+
const source = [5, 6, 7];
|
|
6
|
+
insertSeparator(source, "a").must.eql([5, "a", 6, "a", 7]);
|
|
7
|
+
insertSeparator(source, "a").must.not.equal(source);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("do nothing on array without data (returns source array)", () => {
|
|
11
|
+
const source = [];
|
|
12
|
+
insertSeparator(source, "a").must.equal(source);
|
|
13
|
+
source.length.must.equal(0);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it("do nothing on array with single item (returns source array)", () => {
|
|
17
|
+
const source = [1];
|
|
18
|
+
insertSeparator(source, "a").must.equal(source);
|
|
19
|
+
source.length.must.equal(1);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it("crashes on non-array", () => {
|
|
23
|
+
(() => insertSeparator(null, "a")).must.throw();
|
|
24
|
+
(() => insertSeparator(undefined, "a")).must.throw();
|
|
25
|
+
(() => insertSeparator({}, "a")).must.throw();
|
|
26
|
+
(() => insertSeparator(4, "a")).must.throw();
|
|
27
|
+
(() => insertSeparator("4", "a")).must.throw();
|
|
28
|
+
});
|
|
29
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inserts given separator between all array elements
|
|
3
|
+
*
|
|
4
|
+
* @param {Array} source - source array to put new elements between
|
|
5
|
+
* @param {*} separator - separator to inset
|
|
6
|
+
* @returns {Array} - new array with separator items added or same array if there isn't enough items to put separator
|
|
7
|
+
*/
|
|
8
|
+
const insertSeparator = <T, S>(source: T[], separator: S): (T | S)[] => {
|
|
9
|
+
if (!Array.isArray(source)) {
|
|
10
|
+
throw new TypeError("Source must be an array");
|
|
11
|
+
}
|
|
12
|
+
if (source.length <= 1) {
|
|
13
|
+
return source;
|
|
14
|
+
}
|
|
15
|
+
const result: (T | S)[] = [...source];
|
|
16
|
+
for (let i = result.length - 1; i > 0; i--) {
|
|
17
|
+
result.splice(i, 0, separator);
|
|
18
|
+
}
|
|
19
|
+
return result;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { insertSeparator };
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { isEmpty as _isEmpty } from "lodash";
|
|
2
|
+
|
|
3
|
+
import { isEmpty } from "./isEmpty.js";
|
|
4
|
+
|
|
5
|
+
const emptyMap = new Map();
|
|
6
|
+
const filledMap = new Map();
|
|
7
|
+
filledMap.set("x", "x");
|
|
8
|
+
|
|
9
|
+
const emptySet = new Set();
|
|
10
|
+
const filledSet = new Set();
|
|
11
|
+
filledSet.add("x");
|
|
12
|
+
|
|
13
|
+
const emptyString = "";
|
|
14
|
+
const filledString = "abc";
|
|
15
|
+
|
|
16
|
+
const emptyArray = [];
|
|
17
|
+
const holeyArray = [,]; // eslint-disable-line no-sparse-arrays
|
|
18
|
+
const filledArray = [1];
|
|
19
|
+
|
|
20
|
+
let emptyArgs;
|
|
21
|
+
(function() { emptyArgs = arguments; })(); // eslint-disable-line prefer-rest-params
|
|
22
|
+
let filledArgs;
|
|
23
|
+
(function() { filledArgs = arguments; })(1, 2, 3); // eslint-disable-line prefer-rest-params
|
|
24
|
+
|
|
25
|
+
const emptyBuffer = Buffer.from("");
|
|
26
|
+
const filledBuffer = Buffer.from("abc");
|
|
27
|
+
|
|
28
|
+
const emptyObject = {};
|
|
29
|
+
const filledObject = { a: 5 };
|
|
30
|
+
const nonEnumObject = {};
|
|
31
|
+
Object.defineProperty(nonEnumObject, "prop", { enumerable: false, value: 666 });
|
|
32
|
+
|
|
33
|
+
const SomeElement = function() {};
|
|
34
|
+
SomeElement.prototype.prop = 5;
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
36
|
+
const inheritedObject = new SomeElement();
|
|
37
|
+
|
|
38
|
+
const emptyTypedArray = new Uint8Array();
|
|
39
|
+
const filledTypedArray = new Uint8Array(1);
|
|
40
|
+
filledTypedArray[0] = 69;
|
|
41
|
+
|
|
42
|
+
describe("isEmpty", () => {
|
|
43
|
+
it("works with Maps, behaves like lodash", () => {
|
|
44
|
+
isEmpty(emptyMap).must.be.true();
|
|
45
|
+
_isEmpty(emptyMap).must.be.true();
|
|
46
|
+
|
|
47
|
+
isEmpty(filledMap).must.be.false();
|
|
48
|
+
_isEmpty(filledMap).must.be.false();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it("works with Sets, behaves like lodash", () => {
|
|
52
|
+
isEmpty(emptySet).must.be.true();
|
|
53
|
+
_isEmpty(emptySet).must.be.true();
|
|
54
|
+
|
|
55
|
+
isEmpty(filledSet).must.be.false();
|
|
56
|
+
_isEmpty(filledSet).must.be.false();
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("works with strings, behaves like lodash", () => {
|
|
60
|
+
isEmpty(emptyString).must.be.true();
|
|
61
|
+
_isEmpty(emptyString).must.be.true();
|
|
62
|
+
|
|
63
|
+
isEmpty(filledString).must.be.false();
|
|
64
|
+
_isEmpty(filledString).must.be.false();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("works with arrays, behaves like lodash except for holey arrays", () => {
|
|
68
|
+
isEmpty(emptyArray).must.be.true();
|
|
69
|
+
_isEmpty(emptyArray).must.be.true();
|
|
70
|
+
|
|
71
|
+
isEmpty(filledArray).must.be.false();
|
|
72
|
+
_isEmpty(filledArray).must.be.false();
|
|
73
|
+
|
|
74
|
+
isEmpty(holeyArray).must.be.true();
|
|
75
|
+
_isEmpty(holeyArray).must.be.false();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("works with arguments, behaves like lodash", () => {
|
|
79
|
+
isEmpty(emptyArgs).must.be.true();
|
|
80
|
+
_isEmpty(emptyArgs).must.be.true();
|
|
81
|
+
|
|
82
|
+
isEmpty(filledArgs).must.be.false();
|
|
83
|
+
_isEmpty(filledArgs).must.be.false();
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it("works with Buffers, behaves like lodash", () => {
|
|
87
|
+
isEmpty(emptyBuffer).must.be.true();
|
|
88
|
+
_isEmpty(emptyBuffer).must.be.true();
|
|
89
|
+
|
|
90
|
+
isEmpty(filledBuffer).must.be.false();
|
|
91
|
+
_isEmpty(filledBuffer).must.be.false();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("works with objects, behaves like lodash", () => {
|
|
95
|
+
isEmpty(emptyObject).must.be.true();
|
|
96
|
+
_isEmpty(emptyObject).must.be.true();
|
|
97
|
+
|
|
98
|
+
isEmpty(filledObject).must.be.false();
|
|
99
|
+
_isEmpty({ a: 555, b: 666 }).must.be.false();
|
|
100
|
+
|
|
101
|
+
isEmpty(nonEnumObject).must.be.true();
|
|
102
|
+
_isEmpty(nonEnumObject).must.be.true();
|
|
103
|
+
|
|
104
|
+
isEmpty(inheritedObject).must.be.true();
|
|
105
|
+
_isEmpty(inheritedObject).must.be.true();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("works with typed arrays, behaves like lodash", () => {
|
|
109
|
+
isEmpty(emptyTypedArray).must.be.true();
|
|
110
|
+
_isEmpty(emptyTypedArray).must.be.true();
|
|
111
|
+
|
|
112
|
+
isEmpty(filledTypedArray).must.be.false();
|
|
113
|
+
_isEmpty(filledTypedArray).must.be.false();
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("returns true for nil values", function() {
|
|
117
|
+
isEmpty(null).must.be.true();
|
|
118
|
+
isEmpty(undefined).must.be.true();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it("throws on primitives", () => {
|
|
122
|
+
(() => isEmpty(0)).must.throw();
|
|
123
|
+
(() => isEmpty(100)).must.throw();
|
|
124
|
+
(() => isEmpty(true)).must.throw();
|
|
125
|
+
(() => isEmpty(false)).must.throw();
|
|
126
|
+
(() => isEmpty(Infinity)).must.throw();
|
|
127
|
+
(() => isEmpty(NaN)).must.throw();
|
|
128
|
+
(() => isEmpty(() => {})).must.throw();
|
|
129
|
+
});
|
|
130
|
+
});
|
package/src/isEmpty.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
interface ObjectWithLength {
|
|
2
|
+
length: number;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
interface ObjectWithSize {
|
|
6
|
+
size: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Returns true if passed argument seems to be empty.
|
|
11
|
+
* Nil values are empty.
|
|
12
|
+
* Strings are considered empty when length is 0.
|
|
13
|
+
* Other primitives will throw an error.
|
|
14
|
+
* Objects are considered empty when doesn't have any enumerable & own property.
|
|
15
|
+
* Arrays and array-like objects are considered empty when length value is 0.
|
|
16
|
+
* Map, Set and -like objects are considered empty when size value is 0.
|
|
17
|
+
*
|
|
18
|
+
* @param {*} obj - source value
|
|
19
|
+
* @example isEmpty({}) // true
|
|
20
|
+
* @example isEmpty(100) // throws
|
|
21
|
+
* @example isEmpty([]) // true
|
|
22
|
+
* @example isEmpty([1]) // false
|
|
23
|
+
* @example isEmpty({ length: 5 }) // false
|
|
24
|
+
* @example isEmpty({ length: 0 }) // true
|
|
25
|
+
* @example isEmpty({ size: 0 }) // true
|
|
26
|
+
* @returns {boolean} - is value considered empty
|
|
27
|
+
*/
|
|
28
|
+
const isEmpty = (obj: unknown) => {
|
|
29
|
+
if (typeof obj === "string") {
|
|
30
|
+
return !obj.length;
|
|
31
|
+
}
|
|
32
|
+
if (obj == null) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
if (typeof obj !== "object") {
|
|
36
|
+
throw new Error("isEmpty cannot be used on primitives");
|
|
37
|
+
}
|
|
38
|
+
if (Array.isArray(obj)) {
|
|
39
|
+
return !Object.keys(obj).length;
|
|
40
|
+
}
|
|
41
|
+
if ("length" in obj) {
|
|
42
|
+
return !(obj as ObjectWithLength).length;
|
|
43
|
+
}
|
|
44
|
+
if ("size" in obj) {
|
|
45
|
+
return !(obj as ObjectWithSize).size;
|
|
46
|
+
}
|
|
47
|
+
return !Object.keys(obj).length;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export { isEmpty };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { isPlainObject } from "./isPlainObject.js";
|
|
2
|
+
|
|
3
|
+
describe("isPlainObject", function() {
|
|
4
|
+
it("rejects anything non object-like", function() {
|
|
5
|
+
isPlainObject(null).must.be.false();
|
|
6
|
+
isPlainObject(5).must.be.false();
|
|
7
|
+
isPlainObject(() => ({})).must.be.false();
|
|
8
|
+
isPlainObject(false).must.be.false();
|
|
9
|
+
isPlainObject(new URL("https://ezez.dev")).must.be.false();
|
|
10
|
+
isPlainObject([]).must.be.false();
|
|
11
|
+
|
|
12
|
+
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
|
13
|
+
class X {}
|
|
14
|
+
isPlainObject(X).must.be.false();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it("accepts plain objects", function() {
|
|
18
|
+
isPlainObject({}).must.be.true();
|
|
19
|
+
isPlainObject({ some: "data" }).must.be.true();
|
|
20
|
+
isPlainObject({ some: function fn() {} }).must.be.true();
|
|
21
|
+
isPlainObject(Object.create(null)).must.be.true();
|
|
22
|
+
isPlainObject({ constructor: () => null }).must.be.true();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("accepts plain objects", function() {
|
|
26
|
+
isPlainObject({}).must.be.true();
|
|
27
|
+
isPlainObject({ some: "data" }).must.be.true();
|
|
28
|
+
isPlainObject({ some: function fn() {} }).must.be.true();
|
|
29
|
+
isPlainObject(Object.create(null)).must.be.true();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("shouldn't accept any instances", function() {
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-extraneous-class
|
|
34
|
+
class X {}
|
|
35
|
+
isPlainObject(new X()).must.be.false();
|
|
36
|
+
|
|
37
|
+
// eslint-disable-next-line func-style
|
|
38
|
+
function MyObject() {}
|
|
39
|
+
// @ts-expect-error we don't care
|
|
40
|
+
isPlainObject(new MyObject()).must.be.false();
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if given value is a plain object. Plain object should be an object that's not an instance of anything.
|
|
3
|
+
* @param value - value to test
|
|
4
|
+
*/
|
|
5
|
+
const isPlainObject = (value: unknown) => Boolean(
|
|
6
|
+
value
|
|
7
|
+
&& typeof value === "object"
|
|
8
|
+
&& (
|
|
9
|
+
value.constructor === Object // default constructor
|
|
10
|
+
|| !("constructor" in value) // no constructor
|
|
11
|
+
|| Object.getOwnPropertyNames(value).includes("constructor") // constructor is set by user
|
|
12
|
+
),
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
isPlainObject,
|
|
17
|
+
};
|
|
18
|
+
|