deep-guards 1.0.2 → 1.0.4
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 +22 -3
- package/dist/compound.d.ts +8 -1
- package/dist/compound.js +30 -14
- package/dist/compound.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- package/dist/helpers.d.ts +9 -0
- package/dist/helpers.js +8 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +6 -5
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/macros.d.ts +8 -0
- package/dist/macros.js +8 -0
- package/dist/macros.js.map +1 -0
- package/dist/primitives.d.ts +1 -1
- package/dist/structures.d.ts +5 -3
- package/dist/structures.js +20 -12
- package/dist/structures.js.map +1 -1
- package/dist/types.d.ts +0 -3
- package/package.json +5 -5
- package/src/compound.ts +24 -19
- package/src/errors.ts +17 -17
- package/src/helpers.ts +22 -0
- package/src/index.ts +6 -5
- package/src/macros.ts +25 -8
- package/src/primitives.ts +23 -23
- package/src/structures.ts +43 -18
- package/src/types.ts +0 -4
- package/tests/compound.test.ts +5 -4
- package/tests/macros.test.ts +38 -0
- package/tests/primitives.test.ts +91 -91
- package/tests/structures.test.ts +173 -110
package/README.md
CHANGED
|
@@ -58,8 +58,9 @@ if (vehicleGuard(value)) {
|
|
|
58
58
|
1. [isAnyArray](#isanyarray)
|
|
59
59
|
2. [isAnyRecord](#isanyrecord)
|
|
60
60
|
3. [isArrayOf](#isarrayof)
|
|
61
|
-
4. [
|
|
62
|
-
5. [
|
|
61
|
+
4. [isTupleOf](#istupleof)
|
|
62
|
+
5. [isRecordOf](#isrecordof)
|
|
63
|
+
6. [isObjectOf](#isobjectof)
|
|
63
64
|
4. [Macros](#macros)
|
|
64
65
|
1. [isDiscriminatedObjectOf](#isdiscriminatedobjectof)
|
|
65
66
|
5. [guardOrThrow](#guardorthrow)
|
|
@@ -160,6 +161,21 @@ Higher order guard. This will pass if the incoming value is an array which conta
|
|
|
160
161
|
|
|
161
162
|
NOTE: This passes for empty arrays
|
|
162
163
|
|
|
164
|
+
### isTupleOf
|
|
165
|
+
|
|
166
|
+
Higher order guard. This takes in any number of guards, and then checks that the incoming value is an array of the same size, with the guards guarding the items in the same order as they appear.
|
|
167
|
+
|
|
168
|
+
For example:
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
const myTupleGuard = isTupleOf(isNumber, isString, isBoolean);
|
|
172
|
+
const value: unknown = [1, "foo", true];
|
|
173
|
+
|
|
174
|
+
if (myTupleGuard(value)) {
|
|
175
|
+
// value passes
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
163
179
|
### isRecordOf
|
|
164
180
|
|
|
165
181
|
Higher order guard. It has two guard parameters, where the first is the key guard, and then the second is a value guard which is optional. If you don't pass in a value guard, the returned guard function has `unknown`s as the value type.
|
|
@@ -170,6 +186,8 @@ NOTE: This passes for empty records
|
|
|
170
186
|
|
|
171
187
|
This is a function which takes in a structured object, containing keys of type `string | number | symbol`, and then values which are guard functions.
|
|
172
188
|
|
|
189
|
+
This also takes in a second boolean parameter for if the guard should check that the keys exactly match the structured guard object's keys. It then doesn't let any "leaky" objects through, which have extra keys. This defaults to false.
|
|
190
|
+
|
|
173
191
|
As seen in the example at the start of this readme, you can do all sorts of complex nesting, as this produces a guard in the end.
|
|
174
192
|
|
|
175
193
|
NOTE: This throws an error if you give it an empty object.\
|
|
@@ -181,7 +199,8 @@ These are common use cases for guarding setups, where they are made entirely out
|
|
|
181
199
|
|
|
182
200
|
### isDiscriminatedObjectOf
|
|
183
201
|
|
|
184
|
-
This takes in a string literal
|
|
202
|
+
This takes in a string literal for the discriminated value, and an `isObjectOf` guard, and then an optional key specifying which key is for the discriminated union. This then combines the object with the discriminator, where the returned guard has the signature: `Guard<{ [key]: T } & O>`.
|
|
203
|
+
|
|
185
204
|
This is good for use cases where you don't have a discriminator on an individual type, but then do have it on the union type. For example:
|
|
186
205
|
|
|
187
206
|
```ts
|
package/dist/compound.d.ts
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GuardSchemaOf } from "./helpers";
|
|
2
|
+
import { Guard } from "./types";
|
|
2
3
|
export declare function isOptional<T>(guard: Guard<T>): Guard<T | undefined>;
|
|
3
4
|
export declare function isNullable<T>(guard: Guard<T>): Guard<T | null | undefined>;
|
|
4
5
|
export declare function isNonNullable<T>(value: T | null | undefined): value is T;
|
|
5
6
|
export declare function isNot<const N>(guard: Guard<N>): <const T>(value: T | N) => value is T;
|
|
6
7
|
export declare function isOneOf<const T extends (string | number | boolean | symbol | null | undefined)[]>(...values: T): Guard<(typeof values)[number]>;
|
|
7
8
|
export declare function isUnionOf<T extends readonly unknown[]>(...guards: GuardSchemaOf<T>): Guard<T[number]>;
|
|
9
|
+
type ArrayToIntersection<A extends readonly unknown[]> = A extends [
|
|
10
|
+
infer T,
|
|
11
|
+
...infer R
|
|
12
|
+
] ? T & ArrayToIntersection<R> : unknown;
|
|
13
|
+
export declare function isIntersectionOf<T extends readonly unknown[]>(...guards: GuardSchemaOf<T>): Guard<ArrayToIntersection<T>>;
|
|
8
14
|
export declare function isExact<const T>(expected: T, deep?: boolean): Guard<T>;
|
|
15
|
+
export {};
|
package/dist/compound.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import { objectKeys } from "./helpers";
|
|
1
2
|
export function isOptional(guard) {
|
|
2
3
|
if (typeof guard !== "function") {
|
|
3
4
|
throw new TypeError(`isOptional expects a guard parameter. Got instead: ${guard}`);
|
|
4
5
|
}
|
|
5
|
-
return (
|
|
6
|
+
return (value) => value === undefined || guard(value);
|
|
6
7
|
}
|
|
7
8
|
export function isNullable(guard) {
|
|
8
9
|
if (typeof guard !== "function") {
|
|
@@ -29,20 +30,35 @@ export function isUnionOf(...guards) {
|
|
|
29
30
|
}
|
|
30
31
|
return (value) => guards.some((guard) => guard(value));
|
|
31
32
|
}
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
(
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
Object.entries(a).every(([k, v]) => k in b && isEqual(v, b[k])))));
|
|
33
|
+
export function isIntersectionOf(...guards) {
|
|
34
|
+
if (guards.every((guard) => typeof guard !== "function")) {
|
|
35
|
+
throw new TypeError(`isIntersectionOf expects N guard parameters. Got instead: ${guards}`);
|
|
36
|
+
}
|
|
37
|
+
return (value) => guards.every((guard) => guard(value));
|
|
38
|
+
}
|
|
39
|
+
function objectEntriesChecks(a, b) {
|
|
40
|
+
const aKeys = objectKeys(a);
|
|
41
|
+
const bKeySet = new Set(objectKeys(b));
|
|
42
|
+
return (aKeys.length === bKeySet.size &&
|
|
43
|
+
aKeys.every((k) => bKeySet.has(k) && isExact(a[k], true)(b[k])));
|
|
44
44
|
}
|
|
45
45
|
export function isExact(expected, deep = true) {
|
|
46
|
-
return (value) =>
|
|
46
|
+
return (value) =>
|
|
47
|
+
// Shallow checks
|
|
48
|
+
expected === value ||
|
|
49
|
+
(Number.isNaN(expected) && Number.isNaN(value)) ||
|
|
50
|
+
(deep &&
|
|
51
|
+
(Array.isArray(expected)
|
|
52
|
+
? // Array checks
|
|
53
|
+
Array.isArray(value) &&
|
|
54
|
+
expected.length === value.length &&
|
|
55
|
+
expected.every((v, i) => isExact(v, true)(value[i]))
|
|
56
|
+
: // Object checks
|
|
57
|
+
expected != null &&
|
|
58
|
+
value != null &&
|
|
59
|
+
typeof expected === "object" &&
|
|
60
|
+
typeof value === "object" &&
|
|
61
|
+
!Array.isArray(value) &&
|
|
62
|
+
objectEntriesChecks(expected, value)));
|
|
47
63
|
}
|
|
48
64
|
//# sourceMappingURL=compound.js.map
|
package/dist/compound.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compound.js","sourceRoot":"src/","sources":["compound.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"compound.js","sourceRoot":"src/","sources":["compound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,UAAU,EAAE,MAAM,WAAW,CAAC;AAGtD,MAAM,UAAU,UAAU,CAAI,KAAe;IAC3C,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CACjB,sDAAsD,KAAK,EAAE,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAA0B,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,UAAU,CAAI,KAAe;IAC3C,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CACjB,sDAAsD,KAAK,EAAE,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAc,EAAiC,EAAE,CACvD,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAAI,KAA2B;IAC1D,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,KAAK,CAAU,KAAe;IAC5C,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CACjB,iDAAiD,KAAK,EAAE,CACzD,CAAC;IACJ,CAAC;IAED,OAAO,CAAU,KAAY,EAAc,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,OAAO,CAErB,GAAG,MAAS;IACZ,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,KAAc,EAAsB,EAAE,CAC5C,QAAQ,CAAC,GAAG,CAAC,KAAkB,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,GAAG,MAAwB;IAE3B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CACjB,sDAAsD,MAAM,EAAE,CAC/D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAc,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AACrE,CAAC;AASD,MAAM,UAAU,gBAAgB,CAC9B,GAAG,MAAwB;IAE3B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CACjB,6DAA6D,MAAM,EAAE,CACtE,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAmC,EAAE,CAChD,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,SAAS,mBAAmB,CAAmB,CAAI,EAAE,CAAS;IAC5D,MAAM,KAAK,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;IAC5B,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACvC,OAAO,CACL,KAAK,CAAC,MAAM,KAAK,OAAO,CAAC,IAAI;QAC7B,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAChE,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,OAAO,CAAU,QAAW,EAAE,OAAgB,IAAI;IAChE,OAAO,CAAC,KAAK,EAAc,EAAE;IAC3B,iBAAiB;IACjB,QAAQ,KAAK,KAAK;QAClB,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC,IAAI;YACH,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC;gBACtB,CAAC,CAAC,eAAe;oBACf,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;wBACpB,QAAQ,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM;wBAChC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;gBACtD,CAAC,CAAC,gBAAgB;oBAChB,QAAQ,IAAI,IAAI;wBAChB,KAAK,IAAI,IAAI;wBACb,OAAO,QAAQ,KAAK,QAAQ;wBAC5B,OAAO,KAAK,KAAK,QAAQ;wBACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;wBACrB,mBAAmB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Guard } from "./types";
|
|
2
|
+
export type GuardSchemaOf<O extends object> = {
|
|
3
|
+
[K in keyof O]: Guard<O[K]>;
|
|
4
|
+
};
|
|
5
|
+
export type ObjectKey = string | number | symbol;
|
|
6
|
+
export declare const objectKeys: <K extends ObjectKey>(obj: Record<K, unknown>) => K[];
|
|
7
|
+
export declare function omit<O extends object>(obj: O, key: keyof O): {
|
|
8
|
+
[K in keyof O as K extends typeof key ? never : K]: O[K];
|
|
9
|
+
};
|
package/dist/helpers.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const objectKeys = (obj) => Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj));
|
|
2
|
+
export function omit(obj, key) {
|
|
3
|
+
const omitted = { ...obj };
|
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
5
|
+
delete omitted[key];
|
|
6
|
+
return omitted;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"src/","sources":["helpers.ts"],"names":[],"mappings":"AAQA,MAAM,CAAC,MAAM,UAAU,GAAG,CAAsB,GAAuB,EAAO,EAAE,CAC7E,MAAM,CAAC,mBAAmB,CAAC,GAAG,CAAS,CAAC,MAAM,CAC7C,MAAM,CAAC,qBAAqB,CAAC,GAAG,CAAQ,CACzC,CAAC;AAEJ,MAAM,UAAU,IAAI,CAClB,GAAM,EACN,GAAY;IAEZ,MAAM,OAAO,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC;IAC3B,gEAAgE;IAChE,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC;IACpB,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export * from "./compound
|
|
2
|
-
export * from "./errors
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
1
|
+
export * from "./compound";
|
|
2
|
+
export * from "./errors";
|
|
3
|
+
export * from "./macros";
|
|
4
|
+
export * from "./primitives";
|
|
5
|
+
export * from "./structures";
|
|
6
|
+
export * from "./types";
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
export * from "./compound
|
|
2
|
-
export * from "./errors
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
1
|
+
export * from "./compound";
|
|
2
|
+
export * from "./errors";
|
|
3
|
+
export * from "./macros";
|
|
4
|
+
export * from "./primitives";
|
|
5
|
+
export * from "./structures";
|
|
6
|
+
export * from "./types";
|
|
6
7
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC"}
|
package/dist/macros.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ObjectKey } from "./helpers";
|
|
2
|
+
import { Guard } from "./types";
|
|
3
|
+
export declare function isDiscriminatedObjectOf<const T extends string, O extends object>(value: T, guard: Guard<O>): Guard<{
|
|
4
|
+
type: T;
|
|
5
|
+
} & O>;
|
|
6
|
+
export declare function isDiscriminatedObjectOf<const T extends string, O extends object, const K extends ObjectKey>(value: T, guard: Guard<O>, key: K): Guard<{
|
|
7
|
+
[S in K]: T;
|
|
8
|
+
} & O>;
|
package/dist/macros.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { isExact } from "./compound";
|
|
2
|
+
import { omit } from "./helpers";
|
|
3
|
+
import { isObjectOf } from "./structures";
|
|
4
|
+
export function isDiscriminatedObjectOf(value, guard, key = "type") {
|
|
5
|
+
const discriminatorGuard = isObjectOf({ [key]: isExact(value) });
|
|
6
|
+
return (value) => discriminatorGuard(value) && guard(omit(value, key));
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=macros.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"macros.js","sourceRoot":"src/","sources":["macros.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrC,OAAO,EAAa,IAAI,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAY1C,MAAM,UAAU,uBAAuB,CAIrC,KAAQ,EACR,KAAe,EACf,MAAiB,MAAM;IAEvB,MAAM,kBAAkB,GAAG,UAAU,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,EAAE,CAE7D,CAAC;IACH,OAAO,CAAC,KAAK,EAAyC,EAAE,CACtD,kBAAkB,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAC;AACzD,CAAC"}
|
package/dist/primitives.d.ts
CHANGED
package/dist/structures.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import { GuardSchemaOf, ObjectKey } from "./helpers";
|
|
2
|
+
import { Guard } from "./types";
|
|
3
3
|
export declare const isAnyArray: Guard<unknown[]>;
|
|
4
4
|
export declare const isAnyRecord: Guard<Record<ObjectKey, unknown>>;
|
|
5
5
|
export declare function isArrayOf<T>(guard: Guard<T>): Guard<T[]>;
|
|
6
|
+
export declare function isTupleOf<T extends readonly unknown[]>(...tupleGuards: GuardSchemaOf<T>): Guard<T>;
|
|
6
7
|
export declare function isRecordOf<K extends ObjectKey>(keyGuard: Guard<K>): Guard<Record<K, unknown>>;
|
|
7
8
|
export declare function isRecordOf<K extends ObjectKey, V>(keyGuard: Guard<K>, valueGuard: Guard<V>): Guard<Record<K, V>>;
|
|
8
|
-
|
|
9
|
+
type IsObjectOfGuard<O extends object> = O extends unknown[] ? never : {} extends O ? never : Guard<O>;
|
|
10
|
+
export declare function isObjectOf<O extends object>(schema: GuardSchemaOf<O>, exactKeys?: boolean): IsObjectOfGuard<O>;
|
|
9
11
|
export {};
|
package/dist/structures.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj));
|
|
3
|
-
}
|
|
1
|
+
import { objectKeys } from "./helpers";
|
|
4
2
|
export const isAnyArray = (value) => Array.isArray(value);
|
|
5
3
|
export const isAnyRecord = (value) => value != null && typeof value === "object" && !Array.isArray(value);
|
|
6
4
|
export function isArrayOf(guard) {
|
|
@@ -9,6 +7,14 @@ export function isArrayOf(guard) {
|
|
|
9
7
|
}
|
|
10
8
|
return (value) => Array.isArray(value) && value.every(guard);
|
|
11
9
|
}
|
|
10
|
+
export function isTupleOf(...tupleGuards) {
|
|
11
|
+
if (tupleGuards.some((guard) => typeof guard !== "function")) {
|
|
12
|
+
throw new TypeError(`isTupleOf expects guard parameters. Got instead: ${JSON.stringify(tupleGuards)}`);
|
|
13
|
+
}
|
|
14
|
+
return (value) => Array.isArray(value) &&
|
|
15
|
+
value.length === tupleGuards.length &&
|
|
16
|
+
tupleGuards.every((guard, i) => guard(value[i]));
|
|
17
|
+
}
|
|
12
18
|
export function isRecordOf(keyGuard, valueGuard) {
|
|
13
19
|
if (typeof keyGuard !== "function") {
|
|
14
20
|
throw new TypeError(`isRecordOf keyGuard expects a guard parameter. Got instead: ${keyGuard}`);
|
|
@@ -21,21 +27,23 @@ export function isRecordOf(keyGuard, valueGuard) {
|
|
|
21
27
|
!Array.isArray(value) &&
|
|
22
28
|
objectKeys(value).every((key) => keyGuard(key) && (valueGuard?.(value[key]) ?? true));
|
|
23
29
|
}
|
|
24
|
-
export function isObjectOf(schema) {
|
|
25
|
-
const schemaKeys = objectKeys(schema);
|
|
30
|
+
export function isObjectOf(schema, exactKeys = false) {
|
|
26
31
|
const schemaUnknown = schema;
|
|
27
|
-
if (
|
|
28
|
-
throw new
|
|
32
|
+
if (schemaUnknown == null || typeof schemaUnknown !== "object") {
|
|
33
|
+
throw new TypeError(`isObjectOf expects a guard schema object. Got instead: ${schemaUnknown}`);
|
|
34
|
+
}
|
|
35
|
+
const schemaKeys = objectKeys(schema);
|
|
36
|
+
if (Array.isArray(schema) ||
|
|
37
|
+
schemaKeys.some((key) => typeof schema[key] !== "function")) {
|
|
38
|
+
throw new TypeError(`isObjectOf expects a guard schema object. Got instead ${JSON.stringify(schema)}`);
|
|
29
39
|
}
|
|
30
|
-
else if (
|
|
31
|
-
|
|
32
|
-
Array.isArray(schemaUnknown) ||
|
|
33
|
-
schemaKeys.some((key) => typeof schemaUnknown[key] !== "function")) {
|
|
34
|
-
throw new TypeError(`isObjectOf expects a guard schema. Got instead: ${schemaUnknown}`);
|
|
40
|
+
else if (schemaKeys.length === 0) {
|
|
41
|
+
throw new Error("isObjectOf received an empty schema");
|
|
35
42
|
}
|
|
36
43
|
return ((value) => value != null &&
|
|
37
44
|
typeof value === "object" &&
|
|
38
45
|
!Array.isArray(value) &&
|
|
46
|
+
(!exactKeys || schemaKeys.length === objectKeys(value).length) &&
|
|
39
47
|
schemaKeys.every((key) => key in value && schema[key](value[key])));
|
|
40
48
|
}
|
|
41
49
|
//# sourceMappingURL=structures.js.map
|
package/dist/structures.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structures.js","sourceRoot":"src/","sources":["structures.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"structures.js","sourceRoot":"src/","sources":["structures.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,UAAU,EAAE,MAAM,WAAW,CAAC;AAGjE,MAAM,CAAC,MAAM,UAAU,GAAqB,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,WAAW,GAAsC,CAC5D,KAAK,EACgC,EAAE,CACvC,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEtE,MAAM,UAAU,SAAS,CAAI,KAAe;IAC1C,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CACjB,qDAAqD,KAAK,EAAE,CAC7D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAgB,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,GAAG,WAA6B;IAEhC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,SAAS,CACjB,oDAAoD,IAAI,CAAC,SAAS,CAChE,WAAW,CACZ,EAAE,CACJ,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAc,EAAE,CAC3B,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,KAAK,CAAC,MAAM,KAAK,WAAW,CAAC,MAAM;QACnC,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACrD,CAAC;AASD,MAAM,UAAU,UAAU,CACxB,QAAkB,EAClB,UAAqB;IAErB,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CACjB,+DAA+D,QAAQ,EAAE,CAC1E,CAAC;IACJ,CAAC;SAAM,IAAI,UAAU,IAAI,IAAI,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;QAClE,MAAM,IAAI,SAAS,CACjB,2EAA2E,UAAU,EAAE,CACxF,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAyB,EAAE,CACtC,KAAK,IAAI,IAAI;QACb,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,CACrB,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAC7D,CAAC;AACN,CAAC;AASD,MAAM,UAAU,UAAU,CACxB,MAAwB,EACxB,YAAqB,KAAK;IAE1B,MAAM,aAAa,GAAY,MAAM,CAAC;IACtC,IAAI,aAAa,IAAI,IAAI,IAAI,OAAO,aAAa,KAAK,QAAQ,EAAE,CAAC;QAC/D,MAAM,IAAI,SAAS,CACjB,0DAA0D,aAAa,EAAE,CAC1E,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,IACE,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QACrB,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,EAC3D,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,yDAAyD,IAAI,CAAC,SAAS,CACrE,MAAM,CACP,EAAE,CACJ,CAAC;IACJ,CAAC;SAAM,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,OAAO,CAAC,CAAC,KAAK,EAAc,EAAE,CAC5B,KAAK,IAAI,IAAI;QACb,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,CAAC,CAAC,SAAS,IAAI,UAAU,CAAC,MAAM,KAAK,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;QAC9D,UAAU,CAAC,KAAK,CACd,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,MAAM,CAAC,GAAG,CAAC,CAAE,KAAW,CAAC,GAAG,CAAC,CAAC,CACxD,CAAuB,CAAC;AAC7B,CAAC"}
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"type": "module",
|
|
9
9
|
"types": "dist/index.d.ts",
|
|
10
10
|
"homepage": "https://github.com/eniallator/Deep-Guards",
|
|
11
|
-
"version": "1.0.
|
|
11
|
+
"version": "1.0.4",
|
|
12
12
|
"scripts": {
|
|
13
13
|
"test": "jest",
|
|
14
14
|
"build": "tsc",
|
|
@@ -28,15 +28,15 @@
|
|
|
28
28
|
"@types/eslint": "^9.6.1",
|
|
29
29
|
"@types/eslint__js": "^8.42.3",
|
|
30
30
|
"@types/jest": "^29.5.14",
|
|
31
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
32
|
-
"@typescript-eslint/parser": "^8.
|
|
31
|
+
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
|
32
|
+
"@typescript-eslint/parser": "^8.16.0",
|
|
33
33
|
"babel-jest": "^29.7.0",
|
|
34
34
|
"eslint": "^9.15.0",
|
|
35
35
|
"eslint-plugin-eslint-comments": "^3.2.0",
|
|
36
36
|
"eslint-plugin-import": "^2.31.0",
|
|
37
37
|
"eslint-plugin-jest": "^28.9.0",
|
|
38
38
|
"jest": "^29.7.0",
|
|
39
|
-
"typescript": "^5.
|
|
40
|
-
"typescript-eslint": "^8.
|
|
39
|
+
"typescript": "^5.7.2",
|
|
40
|
+
"typescript-eslint": "^8.16.0"
|
|
41
41
|
}
|
|
42
42
|
}
|
package/src/compound.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { GuardSchemaOf, objectKeys } from "./helpers";
|
|
2
|
+
import { Guard } from "./types";
|
|
2
3
|
|
|
3
4
|
export function isOptional<T>(guard: Guard<T>): Guard<T | undefined> {
|
|
4
5
|
if (typeof guard !== "function") {
|
|
@@ -7,9 +8,7 @@ export function isOptional<T>(guard: Guard<T>): Guard<T | undefined> {
|
|
|
7
8
|
);
|
|
8
9
|
}
|
|
9
10
|
|
|
10
|
-
return (
|
|
11
|
-
T | undefined
|
|
12
|
-
>;
|
|
11
|
+
return (value): value is T | undefined => value === undefined || guard(value);
|
|
13
12
|
}
|
|
14
13
|
|
|
15
14
|
export function isNullable<T>(guard: Guard<T>): Guard<T | null | undefined> {
|
|
@@ -77,25 +76,31 @@ export function isIntersectionOf<T extends readonly unknown[]>(
|
|
|
77
76
|
guards.every((guard) => guard(value));
|
|
78
77
|
}
|
|
79
78
|
|
|
80
|
-
function
|
|
79
|
+
function objectEntriesChecks<T extends object>(a: T, b: object): b is T {
|
|
80
|
+
const aKeys = objectKeys(a);
|
|
81
|
+
const bKeySet = new Set(objectKeys(b));
|
|
81
82
|
return (
|
|
82
|
-
|
|
83
|
-
(
|
|
84
|
-
b != null &&
|
|
85
|
-
typeof a === "object" &&
|
|
86
|
-
typeof b === "object" &&
|
|
87
|
-
(Array.isArray(a)
|
|
88
|
-
? Array.isArray(b) &&
|
|
89
|
-
a.length === b.length &&
|
|
90
|
-
a.every((v, i) => isEqual(v, b[i]))
|
|
91
|
-
: Object.keys(a).length === Object.keys(b).length &&
|
|
92
|
-
Object.entries(a).every(
|
|
93
|
-
([k, v]) => k in b && isEqual(v, (b as Record<string, unknown>)[k])
|
|
94
|
-
)))
|
|
83
|
+
aKeys.length === bKeySet.size &&
|
|
84
|
+
aKeys.every((k) => bKeySet.has(k) && isExact(a[k], true)(b[k]))
|
|
95
85
|
);
|
|
96
86
|
}
|
|
97
87
|
|
|
98
88
|
export function isExact<const T>(expected: T, deep: boolean = true): Guard<T> {
|
|
99
89
|
return (value): value is T =>
|
|
100
|
-
|
|
90
|
+
// Shallow checks
|
|
91
|
+
expected === value ||
|
|
92
|
+
(Number.isNaN(expected) && Number.isNaN(value)) ||
|
|
93
|
+
(deep &&
|
|
94
|
+
(Array.isArray(expected)
|
|
95
|
+
? // Array checks
|
|
96
|
+
Array.isArray(value) &&
|
|
97
|
+
expected.length === value.length &&
|
|
98
|
+
expected.every((v, i) => isExact(v, true)(value[i]))
|
|
99
|
+
: // Object checks
|
|
100
|
+
expected != null &&
|
|
101
|
+
value != null &&
|
|
102
|
+
typeof expected === "object" &&
|
|
103
|
+
typeof value === "object" &&
|
|
104
|
+
!Array.isArray(value) &&
|
|
105
|
+
objectEntriesChecks(expected, value)));
|
|
101
106
|
}
|
package/src/errors.ts
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { Guard } from "./types
|
|
2
|
-
|
|
3
|
-
export class GuardError extends Error {
|
|
4
|
-
name = "GuardError";
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function guardOrThrow<T>(
|
|
8
|
-
value: unknown,
|
|
9
|
-
guard: Guard<T>,
|
|
10
|
-
hint?: string
|
|
11
|
-
): T {
|
|
12
|
-
if (guard(value)) {
|
|
13
|
-
return value;
|
|
14
|
-
} else {
|
|
15
|
-
throw new GuardError(hint ?? "Guard error");
|
|
16
|
-
}
|
|
17
|
-
}
|
|
1
|
+
import { Guard } from "./types";
|
|
2
|
+
|
|
3
|
+
export class GuardError extends Error {
|
|
4
|
+
name = "GuardError";
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function guardOrThrow<T>(
|
|
8
|
+
value: unknown,
|
|
9
|
+
guard: Guard<T>,
|
|
10
|
+
hint?: string
|
|
11
|
+
): T {
|
|
12
|
+
if (guard(value)) {
|
|
13
|
+
return value;
|
|
14
|
+
} else {
|
|
15
|
+
throw new GuardError(hint ?? "Guard error");
|
|
16
|
+
}
|
|
17
|
+
}
|
package/src/helpers.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Guard } from "./types";
|
|
2
|
+
|
|
3
|
+
export type GuardSchemaOf<O extends object> = {
|
|
4
|
+
[K in keyof O]: Guard<O[K]>;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export type ObjectKey = string | number | symbol;
|
|
8
|
+
|
|
9
|
+
export const objectKeys = <K extends ObjectKey>(obj: Record<K, unknown>): K[] =>
|
|
10
|
+
(Object.getOwnPropertyNames(obj) as K[]).concat(
|
|
11
|
+
Object.getOwnPropertySymbols(obj) as K[]
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
export function omit<O extends object>(
|
|
15
|
+
obj: O,
|
|
16
|
+
key: keyof O
|
|
17
|
+
): { [K in keyof O as K extends typeof key ? never : K]: O[K] } {
|
|
18
|
+
const omitted = { ...obj };
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
20
|
+
delete omitted[key];
|
|
21
|
+
return omitted;
|
|
22
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
export * from "./compound
|
|
2
|
-
export * from "./errors
|
|
3
|
-
export * from "./
|
|
4
|
-
export * from "./
|
|
5
|
-
export * from "./
|
|
1
|
+
export * from "./compound";
|
|
2
|
+
export * from "./errors";
|
|
3
|
+
export * from "./macros";
|
|
4
|
+
export * from "./primitives";
|
|
5
|
+
export * from "./structures";
|
|
6
|
+
export * from "./types";
|
package/src/macros.ts
CHANGED
|
@@ -1,11 +1,28 @@
|
|
|
1
|
-
import { isExact
|
|
1
|
+
import { isExact } from "./compound";
|
|
2
|
+
import { ObjectKey, omit } from "./helpers";
|
|
2
3
|
import { isObjectOf } from "./structures";
|
|
3
4
|
import { Guard } from "./types";
|
|
4
5
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
): Guard<{ type: T } & O
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
export function isDiscriminatedObjectOf<
|
|
7
|
+
const T extends string,
|
|
8
|
+
O extends object
|
|
9
|
+
>(value: T, guard: Guard<O>): Guard<{ type: T } & O>;
|
|
10
|
+
export function isDiscriminatedObjectOf<
|
|
11
|
+
const T extends string,
|
|
12
|
+
O extends object,
|
|
13
|
+
const K extends ObjectKey
|
|
14
|
+
>(value: T, guard: Guard<O>, key: K): Guard<{ [S in K]: T } & O>;
|
|
15
|
+
export function isDiscriminatedObjectOf<
|
|
16
|
+
const T extends string,
|
|
17
|
+
O extends object
|
|
18
|
+
>(
|
|
19
|
+
value: T,
|
|
20
|
+
guard: Guard<O>,
|
|
21
|
+
key: ObjectKey = "type"
|
|
22
|
+
): Guard<{ [S in typeof key]: T } & O> {
|
|
23
|
+
const discriminatorGuard = isObjectOf({ [key]: isExact(value) }) as Guard<{
|
|
24
|
+
[S in typeof key]: T;
|
|
25
|
+
}>;
|
|
26
|
+
return (value): value is { [S in typeof key]: T } & O =>
|
|
27
|
+
discriminatorGuard(value) && guard(omit(value, key));
|
|
28
|
+
}
|
package/src/primitives.ts
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
import { Guard } from "./types
|
|
2
|
-
|
|
3
|
-
export const isUnknown: Guard<unknown> = (_value): _value is unknown => true;
|
|
4
|
-
|
|
5
|
-
export const isNull: Guard<null> = (value) => value === null;
|
|
6
|
-
|
|
7
|
-
export const isUndefined: Guard<undefined> = (value) => value === undefined;
|
|
8
|
-
|
|
9
|
-
export const isNumber: Guard<number> = (value) => typeof value === "number";
|
|
10
|
-
|
|
11
|
-
export const isInteger: Guard<number> = (value): value is number =>
|
|
12
|
-
Number.isInteger(value);
|
|
13
|
-
|
|
14
|
-
export const isString: Guard<string> = (value) => typeof value === "string";
|
|
15
|
-
|
|
16
|
-
export const isSymbol: Guard<symbol> = (value) => typeof value === "symbol";
|
|
17
|
-
|
|
18
|
-
export const isBoolean: Guard<boolean> = (value) =>
|
|
19
|
-
value === true || value === false;
|
|
20
|
-
|
|
21
|
-
export const isFunction: Guard<(...args: unknown[]) => unknown> = (
|
|
22
|
-
value
|
|
23
|
-
): value is (...args: unknown[]) => unknown => typeof value === "function";
|
|
1
|
+
import { Guard } from "./types";
|
|
2
|
+
|
|
3
|
+
export const isUnknown: Guard<unknown> = (_value): _value is unknown => true;
|
|
4
|
+
|
|
5
|
+
export const isNull: Guard<null> = (value) => value === null;
|
|
6
|
+
|
|
7
|
+
export const isUndefined: Guard<undefined> = (value) => value === undefined;
|
|
8
|
+
|
|
9
|
+
export const isNumber: Guard<number> = (value) => typeof value === "number";
|
|
10
|
+
|
|
11
|
+
export const isInteger: Guard<number> = (value): value is number =>
|
|
12
|
+
Number.isInteger(value);
|
|
13
|
+
|
|
14
|
+
export const isString: Guard<string> = (value) => typeof value === "string";
|
|
15
|
+
|
|
16
|
+
export const isSymbol: Guard<symbol> = (value) => typeof value === "symbol";
|
|
17
|
+
|
|
18
|
+
export const isBoolean: Guard<boolean> = (value) =>
|
|
19
|
+
value === true || value === false;
|
|
20
|
+
|
|
21
|
+
export const isFunction: Guard<(...args: unknown[]) => unknown> = (
|
|
22
|
+
value
|
|
23
|
+
): value is (...args: unknown[]) => unknown => typeof value === "function";
|
package/src/structures.ts
CHANGED
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
type ObjectKey = string | number | symbol;
|
|
4
|
-
|
|
5
|
-
function objectKeys<K extends ObjectKey>(obj: Record<K, unknown>): K[] {
|
|
6
|
-
return (Object.getOwnPropertyNames(obj) as K[]).concat(
|
|
7
|
-
Object.getOwnPropertySymbols(obj) as K[]
|
|
8
|
-
);
|
|
9
|
-
}
|
|
1
|
+
import { GuardSchemaOf, ObjectKey, objectKeys } from "./helpers";
|
|
2
|
+
import { Guard } from "./types";
|
|
10
3
|
|
|
11
4
|
export const isAnyArray: Guard<unknown[]> = (value) => Array.isArray(value);
|
|
12
5
|
|
|
@@ -25,6 +18,23 @@ export function isArrayOf<T>(guard: Guard<T>): Guard<T[]> {
|
|
|
25
18
|
return (value): value is T[] => Array.isArray(value) && value.every(guard);
|
|
26
19
|
}
|
|
27
20
|
|
|
21
|
+
export function isTupleOf<T extends readonly unknown[]>(
|
|
22
|
+
...tupleGuards: GuardSchemaOf<T>
|
|
23
|
+
): Guard<T> {
|
|
24
|
+
if (tupleGuards.some((guard) => typeof guard !== "function")) {
|
|
25
|
+
throw new TypeError(
|
|
26
|
+
`isTupleOf expects guard parameters. Got instead: ${JSON.stringify(
|
|
27
|
+
tupleGuards
|
|
28
|
+
)}`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return (value): value is T =>
|
|
33
|
+
Array.isArray(value) &&
|
|
34
|
+
value.length === tupleGuards.length &&
|
|
35
|
+
tupleGuards.every((guard, i) => guard(value[i]));
|
|
36
|
+
}
|
|
37
|
+
|
|
28
38
|
export function isRecordOf<K extends ObjectKey>(
|
|
29
39
|
keyGuard: Guard<K>
|
|
30
40
|
): Guard<Record<K, unknown>>;
|
|
@@ -55,19 +65,33 @@ export function isRecordOf<K extends ObjectKey, V>(
|
|
|
55
65
|
);
|
|
56
66
|
}
|
|
57
67
|
|
|
68
|
+
type IsObjectOfGuard<O extends object> = O extends unknown[]
|
|
69
|
+
? never
|
|
70
|
+
: // eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
71
|
+
{} extends O
|
|
72
|
+
? never
|
|
73
|
+
: Guard<O>;
|
|
74
|
+
|
|
58
75
|
export function isObjectOf<O extends object>(
|
|
59
|
-
schema: GuardSchemaOf<O
|
|
60
|
-
|
|
76
|
+
schema: GuardSchemaOf<O>,
|
|
77
|
+
exactKeys: boolean = false
|
|
78
|
+
): IsObjectOfGuard<O> {
|
|
79
|
+
const schemaUnknown: unknown = schema;
|
|
80
|
+
if (schemaUnknown == null || typeof schemaUnknown !== "object") {
|
|
81
|
+
throw new TypeError(
|
|
82
|
+
`isObjectOf expects a guard schema object. Got instead: ${schemaUnknown}`
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
61
86
|
const schemaKeys = objectKeys(schema);
|
|
62
|
-
const schemaUnknown = schema as unknown;
|
|
63
87
|
if (
|
|
64
|
-
|
|
65
|
-
typeof
|
|
66
|
-
Array.isArray(schemaUnknown) ||
|
|
67
|
-
schemaKeys.some((key) => typeof (schemaUnknown as O)[key] !== "function")
|
|
88
|
+
Array.isArray(schema) ||
|
|
89
|
+
schemaKeys.some((key) => typeof schema[key] !== "function")
|
|
68
90
|
) {
|
|
69
91
|
throw new TypeError(
|
|
70
|
-
`isObjectOf expects a guard schema. Got instead
|
|
92
|
+
`isObjectOf expects a guard schema object. Got instead ${JSON.stringify(
|
|
93
|
+
schema
|
|
94
|
+
)}`
|
|
71
95
|
);
|
|
72
96
|
} else if (schemaKeys.length === 0) {
|
|
73
97
|
throw new Error("isObjectOf received an empty schema");
|
|
@@ -77,7 +101,8 @@ export function isObjectOf<O extends object>(
|
|
|
77
101
|
value != null &&
|
|
78
102
|
typeof value === "object" &&
|
|
79
103
|
!Array.isArray(value) &&
|
|
104
|
+
(!exactKeys || schemaKeys.length === objectKeys(value).length) &&
|
|
80
105
|
schemaKeys.every(
|
|
81
106
|
(key) => key in value && schema[key]((value as O)[key])
|
|
82
|
-
)) as
|
|
107
|
+
)) as IsObjectOfGuard<O>;
|
|
83
108
|
}
|
package/src/types.ts
CHANGED
package/tests/compound.test.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isExact,
|
|
3
|
+
isIntersectionOf,
|
|
4
|
+
isNonNullable,
|
|
3
5
|
isNot,
|
|
4
6
|
isNullable,
|
|
5
|
-
|
|
7
|
+
isNumber,
|
|
6
8
|
isOneOf,
|
|
7
9
|
isOptional,
|
|
10
|
+
isString,
|
|
8
11
|
isUnionOf,
|
|
9
|
-
|
|
10
|
-
} from "../src/compound";
|
|
11
|
-
import { isNumber, isString } from "../src/primitives";
|
|
12
|
+
} from "../src";
|
|
12
13
|
|
|
13
14
|
describe("isOptional", () => {
|
|
14
15
|
const guard = isOptional(isString);
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { isDiscriminatedObjectOf, isObjectOf, isString } from "../src";
|
|
2
|
+
|
|
3
|
+
describe("isDiscriminatedObjectOf", () => {
|
|
4
|
+
describe("no key override", () => {
|
|
5
|
+
const guard = isDiscriminatedObjectOf(
|
|
6
|
+
"foo",
|
|
7
|
+
isObjectOf({ bar: isString }, true)
|
|
8
|
+
);
|
|
9
|
+
|
|
10
|
+
it("succeeds for an object of the value", () => {
|
|
11
|
+
expect(guard({ type: "foo", bar: "baz" })).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("fails for any other value", () => {
|
|
15
|
+
expect(guard({ type: "foo" })).toBe(false);
|
|
16
|
+
expect(guard({ bar: "baz" })).toBe(false);
|
|
17
|
+
expect(guard(1)).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe("overriding key", () => {
|
|
22
|
+
const guard = isDiscriminatedObjectOf(
|
|
23
|
+
"foo",
|
|
24
|
+
isObjectOf({ bar: isString }, true),
|
|
25
|
+
"test"
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
it("succeeds for an object of the value", () => {
|
|
29
|
+
expect(guard({ test: "foo", bar: "baz" })).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("fails for any other value", () => {
|
|
33
|
+
expect(guard({ test: "foo" })).toBe(false);
|
|
34
|
+
expect(guard({ bar: "baz" })).toBe(false);
|
|
35
|
+
expect(guard(1)).toBe(false);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
});
|
package/tests/primitives.test.ts
CHANGED
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isBoolean,
|
|
3
|
-
isFunction,
|
|
4
|
-
isInteger,
|
|
5
|
-
isNull,
|
|
6
|
-
isNumber,
|
|
7
|
-
isString,
|
|
8
|
-
isUndefined,
|
|
9
|
-
isUnknown,
|
|
10
|
-
} from "../src
|
|
11
|
-
|
|
12
|
-
describe("isUnknown", () => {
|
|
13
|
-
it("succeeds for any value", () => {
|
|
14
|
-
expect(isUnknown("unknown")).toBe(true);
|
|
15
|
-
});
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe("isAnyFunction", () => {
|
|
19
|
-
it("succeeds for a function", () => {
|
|
20
|
-
expect(isFunction(() => {})).toBe(true);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("fails for any other value", () => {
|
|
24
|
-
expect(isFunction(1)).toBe(false);
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
describe("isNull", () => {
|
|
29
|
-
it("succeeds for null", () => {
|
|
30
|
-
expect(isNull(null)).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it("fails for any other value", () => {
|
|
34
|
-
expect(isNull(undefined)).toBe(false);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
describe("isUndefined", () => {
|
|
39
|
-
it("succeeds for undefined", () => {
|
|
40
|
-
expect(isUndefined(undefined)).toBe(true);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it("fails for any other value", () => {
|
|
44
|
-
expect(isUndefined(null)).toBe(false);
|
|
45
|
-
});
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
describe("isNumber", () => {
|
|
49
|
-
it("succeeds for a number", () => {
|
|
50
|
-
expect(isNumber(1.23)).toBe(true);
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it("fails for any other type", () => {
|
|
54
|
-
expect(isNumber("foo")).toBe(false);
|
|
55
|
-
});
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
describe("isInteger", () => {
|
|
59
|
-
it("succeeds for an integer", () => {
|
|
60
|
-
expect(isInteger(1)).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("fails for a non-integer", () => {
|
|
64
|
-
expect(isInteger(1.23)).toBe(false);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
it("fails for any other type", () => {
|
|
68
|
-
expect(isInteger("foo")).toBe(false);
|
|
69
|
-
});
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
describe("isString", () => {
|
|
73
|
-
it("succeeds for a string", () => {
|
|
74
|
-
expect(isString("Foo bar")).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it("fails for other types", () => {
|
|
78
|
-
expect(isString(true)).toBe(false);
|
|
79
|
-
});
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
describe("isBoolean", () => {
|
|
83
|
-
it("succeeds for a boolean", () => {
|
|
84
|
-
expect(isBoolean(true)).toBe(true);
|
|
85
|
-
expect(isBoolean(false)).toBe(true);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("fails for other types", () => {
|
|
89
|
-
expect(isBoolean("foo")).toBe(false);
|
|
90
|
-
});
|
|
91
|
-
});
|
|
1
|
+
import {
|
|
2
|
+
isBoolean,
|
|
3
|
+
isFunction,
|
|
4
|
+
isInteger,
|
|
5
|
+
isNull,
|
|
6
|
+
isNumber,
|
|
7
|
+
isString,
|
|
8
|
+
isUndefined,
|
|
9
|
+
isUnknown,
|
|
10
|
+
} from "../src";
|
|
11
|
+
|
|
12
|
+
describe("isUnknown", () => {
|
|
13
|
+
it("succeeds for any value", () => {
|
|
14
|
+
expect(isUnknown("unknown")).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe("isAnyFunction", () => {
|
|
19
|
+
it("succeeds for a function", () => {
|
|
20
|
+
expect(isFunction(() => {})).toBe(true);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("fails for any other value", () => {
|
|
24
|
+
expect(isFunction(1)).toBe(false);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe("isNull", () => {
|
|
29
|
+
it("succeeds for null", () => {
|
|
30
|
+
expect(isNull(null)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it("fails for any other value", () => {
|
|
34
|
+
expect(isNull(undefined)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("isUndefined", () => {
|
|
39
|
+
it("succeeds for undefined", () => {
|
|
40
|
+
expect(isUndefined(undefined)).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it("fails for any other value", () => {
|
|
44
|
+
expect(isUndefined(null)).toBe(false);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("isNumber", () => {
|
|
49
|
+
it("succeeds for a number", () => {
|
|
50
|
+
expect(isNumber(1.23)).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it("fails for any other type", () => {
|
|
54
|
+
expect(isNumber("foo")).toBe(false);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
describe("isInteger", () => {
|
|
59
|
+
it("succeeds for an integer", () => {
|
|
60
|
+
expect(isInteger(1)).toBe(true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("fails for a non-integer", () => {
|
|
64
|
+
expect(isInteger(1.23)).toBe(false);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it("fails for any other type", () => {
|
|
68
|
+
expect(isInteger("foo")).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("isString", () => {
|
|
73
|
+
it("succeeds for a string", () => {
|
|
74
|
+
expect(isString("Foo bar")).toBe(true);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it("fails for other types", () => {
|
|
78
|
+
expect(isString(true)).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("isBoolean", () => {
|
|
83
|
+
it("succeeds for a boolean", () => {
|
|
84
|
+
expect(isBoolean(true)).toBe(true);
|
|
85
|
+
expect(isBoolean(false)).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("fails for other types", () => {
|
|
89
|
+
expect(isBoolean("foo")).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
});
|
package/tests/structures.test.ts
CHANGED
|
@@ -1,110 +1,173 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
expect(isAnyArray(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
expect(isAnyRecord(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
expect(guard([
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
isAnyArray,
|
|
3
|
+
isAnyRecord,
|
|
4
|
+
isArrayOf,
|
|
5
|
+
isBoolean,
|
|
6
|
+
isNumber,
|
|
7
|
+
isObjectOf,
|
|
8
|
+
isRecordOf,
|
|
9
|
+
isString,
|
|
10
|
+
isSymbol,
|
|
11
|
+
isTupleOf,
|
|
12
|
+
} from "../src";
|
|
13
|
+
|
|
14
|
+
describe("isAnyArray", () => {
|
|
15
|
+
it("succeeds for an array", () => {
|
|
16
|
+
expect(isAnyArray([])).toBe(true);
|
|
17
|
+
expect(isAnyArray(["foo", 1, true, null])).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it("fails for any other value", () => {
|
|
21
|
+
expect(isAnyArray({})).toBe(false);
|
|
22
|
+
expect(isAnyArray(1)).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("isAnyRecord", () => {
|
|
27
|
+
it("succeeds for a record", () => {
|
|
28
|
+
expect(isAnyRecord({})).toBe(true);
|
|
29
|
+
expect(isAnyRecord({ foo: "bar", baz: 1 })).toBe(true);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it("fails for any other value", () => {
|
|
33
|
+
expect(isAnyRecord([])).toBe(false);
|
|
34
|
+
expect(isAnyRecord(1)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("isArrayOf", () => {
|
|
39
|
+
const guard = isArrayOf(isString);
|
|
40
|
+
|
|
41
|
+
it("succeeds for an array of the value", () => {
|
|
42
|
+
expect(guard([])).toBe(true);
|
|
43
|
+
expect(guard(["foo", "bar", "baz"])).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("fails for any other value", () => {
|
|
47
|
+
expect(guard([1, 2, 3])).toBe(false);
|
|
48
|
+
expect(guard(["foo", "bar", null])).toBe(false);
|
|
49
|
+
expect(guard(1)).toBe(false);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("isTupleOf", () => {
|
|
54
|
+
const guard = isTupleOf(isNumber, isString, isBoolean);
|
|
55
|
+
|
|
56
|
+
it("succeeds for an array of the value", () => {
|
|
57
|
+
expect(guard([1, "foo", true])).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("fails for any other value", () => {
|
|
61
|
+
expect(guard([1, "foo", true, null])).toBe(false);
|
|
62
|
+
expect(guard([1, "foo"])).toBe(false);
|
|
63
|
+
expect(guard(1)).toBe(false);
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
describe("isObjectOf", () => {
|
|
68
|
+
describe("leaky", () => {
|
|
69
|
+
const barSymbol = Symbol("bar");
|
|
70
|
+
const guard = isObjectOf({
|
|
71
|
+
foo: isString,
|
|
72
|
+
[barSymbol]: isNumber,
|
|
73
|
+
baz: isObjectOf({
|
|
74
|
+
qux: isSymbol,
|
|
75
|
+
}),
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("succeeds for an object of the value", () => {
|
|
79
|
+
expect(
|
|
80
|
+
guard({
|
|
81
|
+
foo: "hello",
|
|
82
|
+
[barSymbol]: 1,
|
|
83
|
+
baz: { qux: Symbol("world!") },
|
|
84
|
+
quux: "this should not be checked",
|
|
85
|
+
})
|
|
86
|
+
).toBe(true);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("fails for any other value", () => {
|
|
90
|
+
expect(
|
|
91
|
+
guard({
|
|
92
|
+
foo: "hello",
|
|
93
|
+
[barSymbol]: "FAIL",
|
|
94
|
+
baz: { qux: Symbol("world!") },
|
|
95
|
+
})
|
|
96
|
+
).toBe(false);
|
|
97
|
+
expect(guard(1)).toBe(false);
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe("exact keys", () => {
|
|
102
|
+
const barSymbol = Symbol("bar");
|
|
103
|
+
const guard = isObjectOf(
|
|
104
|
+
{
|
|
105
|
+
foo: isString,
|
|
106
|
+
[barSymbol]: isNumber,
|
|
107
|
+
baz: isObjectOf({
|
|
108
|
+
qux: isSymbol,
|
|
109
|
+
}),
|
|
110
|
+
},
|
|
111
|
+
true
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
it("succeeds for an object of the value", () => {
|
|
115
|
+
expect(
|
|
116
|
+
guard({
|
|
117
|
+
foo: "hello",
|
|
118
|
+
[barSymbol]: 1,
|
|
119
|
+
baz: { qux: Symbol("world!") },
|
|
120
|
+
})
|
|
121
|
+
).toBe(true);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("fails for any other value", () => {
|
|
125
|
+
expect(
|
|
126
|
+
guard({
|
|
127
|
+
foo: "hello",
|
|
128
|
+
[barSymbol]: 1,
|
|
129
|
+
baz: { qux: Symbol("world!") },
|
|
130
|
+
quux: "this should be checked",
|
|
131
|
+
})
|
|
132
|
+
).toBe(false);
|
|
133
|
+
expect(
|
|
134
|
+
guard({
|
|
135
|
+
foo: "hello",
|
|
136
|
+
[barSymbol]: "FAIL",
|
|
137
|
+
baz: { qux: Symbol("world!") },
|
|
138
|
+
})
|
|
139
|
+
).toBe(false);
|
|
140
|
+
expect(guard(1)).toBe(false);
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe("isRecordOf", () => {
|
|
146
|
+
describe("with valueGuard", () => {
|
|
147
|
+
const guard = isRecordOf(isString, isNumber);
|
|
148
|
+
|
|
149
|
+
it("succeeds for a record of the key/value types", () => {
|
|
150
|
+
expect(guard({})).toBe(true);
|
|
151
|
+
expect(guard({ foo: 1, bar: 2 })).toBe(true);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it("fails for any other value", () => {
|
|
155
|
+
expect(guard({ foo: "bar", baz: 1 })).toBe(false);
|
|
156
|
+
expect(guard(1)).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe("without valueGuard", () => {
|
|
161
|
+
const guard = isRecordOf(isString);
|
|
162
|
+
|
|
163
|
+
it("succeeds for a record with the key type", () => {
|
|
164
|
+
expect(guard({})).toBe(true);
|
|
165
|
+
expect(guard({ foo: 1, bar: "baz" })).toBe(true);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("fails for any other value", () => {
|
|
169
|
+
expect(guard({ 1: "foo", [Symbol("bar")]: 1 })).toBe(false);
|
|
170
|
+
expect(guard(1)).toBe(false);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
});
|