deep-guards 1.0.1 → 1.0.3

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/src/structures.ts CHANGED
@@ -1,83 +1,105 @@
1
- import { Guard, GuardSchemaOf } from "./types.js";
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
- }
10
-
11
- export const isAnyArray: Guard<unknown[]> = (value) => Array.isArray(value);
12
-
13
- export const isAnyRecord: Guard<Record<ObjectKey, unknown>> = (
14
- value
15
- ): value is Record<ObjectKey, unknown> =>
16
- value != null && typeof value === "object" && !Array.isArray(value);
17
-
18
- export function isArrayOf<T>(guard: Guard<T>): Guard<T[]> {
19
- if (typeof guard !== "function") {
20
- throw new TypeError(
21
- `isArrayOf expects a guard parameter. Got instead: ${guard}`
22
- );
23
- }
24
-
25
- return (value): value is T[] => Array.isArray(value) && value.every(guard);
26
- }
27
-
28
- export function isRecordOf<K extends ObjectKey>(
29
- keyGuard: Guard<K>
30
- ): Guard<Record<K, unknown>>;
31
- export function isRecordOf<K extends ObjectKey, V>(
32
- keyGuard: Guard<K>,
33
- valueGuard: Guard<V>
34
- ): Guard<Record<K, V>>;
35
- export function isRecordOf<K extends ObjectKey, V>(
36
- keyGuard: Guard<K>,
37
- valueGuard?: Guard<V>
38
- ): Guard<Record<K, V>> {
39
- if (typeof keyGuard !== "function") {
40
- throw new TypeError(
41
- `isRecordOf keyGuard expects a guard parameter. Got instead: ${keyGuard}`
42
- );
43
- } else if (valueGuard != null && typeof valueGuard !== "function") {
44
- throw new TypeError(
45
- `isRecordOf valueGuard expects an optional guard parameter. Got instead: ${valueGuard}`
46
- );
47
- }
48
-
49
- return (value): value is Record<K, V> =>
50
- value != null &&
51
- typeof value === "object" &&
52
- !Array.isArray(value) &&
53
- objectKeys(value).every(
54
- (key) => keyGuard(key) && (valueGuard?.(value[key]) ?? true)
55
- );
56
- }
57
-
58
- export function isObjectOf<O extends object>(
59
- schema: GuardSchemaOf<O>
60
- ): O extends unknown[] ? never : Guard<O> {
61
- const schemaKeys = objectKeys(schema);
62
- const schemaUnknown = schema as unknown;
63
- if (schemaKeys.length === 0) {
64
- throw new Error("isObjectOf received an empty schema");
65
- } else if (
66
- schemaUnknown == null ||
67
- typeof schemaUnknown !== "object" ||
68
- Array.isArray(schemaUnknown) ||
69
- schemaKeys.some((key) => typeof (schemaUnknown as O)[key] !== "function")
70
- ) {
71
- throw new TypeError(
72
- `isObjectOf expects a guard schema. Got instead: ${schemaUnknown}`
73
- );
74
- }
75
-
76
- return ((value): value is O =>
77
- value != null &&
78
- typeof value === "object" &&
79
- !Array.isArray(value) &&
80
- schemaKeys.every(
81
- (key) => key in value && schema[key]((value as O)[key])
82
- )) as O extends unknown[] ? never : Guard<O>;
83
- }
1
+ import { Guard, GuardSchemaOf } from "./types";
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
+ }
10
+
11
+ export const isAnyArray: Guard<unknown[]> = (value) => Array.isArray(value);
12
+
13
+ export const isAnyRecord: Guard<Record<ObjectKey, unknown>> = (
14
+ value
15
+ ): value is Record<ObjectKey, unknown> =>
16
+ value != null && typeof value === "object" && !Array.isArray(value);
17
+
18
+ export function isArrayOf<T>(guard: Guard<T>): Guard<T[]> {
19
+ if (typeof guard !== "function") {
20
+ throw new TypeError(
21
+ `isArrayOf expects a guard parameter. Got instead: ${guard}`
22
+ );
23
+ }
24
+
25
+ return (value): value is T[] => Array.isArray(value) && value.every(guard);
26
+ }
27
+
28
+ export function isTupleOf<T extends readonly unknown[]>(
29
+ ...tupleGuards: GuardSchemaOf<T>
30
+ ): Guard<T> {
31
+ if (tupleGuards.some((guard) => typeof guard !== "function")) {
32
+ throw new TypeError(
33
+ `isTupleOf expects guard parameters. Got instead: ${JSON.stringify(
34
+ tupleGuards
35
+ )}`
36
+ );
37
+ }
38
+
39
+ return (value): value is T =>
40
+ Array.isArray(value) &&
41
+ value.length === tupleGuards.length &&
42
+ tupleGuards.every((guard, i) => guard(value[i]));
43
+ }
44
+
45
+ export function isRecordOf<K extends ObjectKey>(
46
+ keyGuard: Guard<K>
47
+ ): Guard<Record<K, unknown>>;
48
+ export function isRecordOf<K extends ObjectKey, V>(
49
+ keyGuard: Guard<K>,
50
+ valueGuard: Guard<V>
51
+ ): Guard<Record<K, V>>;
52
+ export function isRecordOf<K extends ObjectKey, V>(
53
+ keyGuard: Guard<K>,
54
+ valueGuard?: Guard<V>
55
+ ): Guard<Record<K, V>> {
56
+ if (typeof keyGuard !== "function") {
57
+ throw new TypeError(
58
+ `isRecordOf keyGuard expects a guard parameter. Got instead: ${keyGuard}`
59
+ );
60
+ } else if (valueGuard != null && typeof valueGuard !== "function") {
61
+ throw new TypeError(
62
+ `isRecordOf valueGuard expects an optional guard parameter. Got instead: ${valueGuard}`
63
+ );
64
+ }
65
+
66
+ return (value): value is Record<K, V> =>
67
+ value != null &&
68
+ typeof value === "object" &&
69
+ !Array.isArray(value) &&
70
+ objectKeys(value).every(
71
+ (key) => keyGuard(key) && (valueGuard?.(value[key]) ?? true)
72
+ );
73
+ }
74
+
75
+ export function isObjectOf<O extends object>(
76
+ schema: GuardSchemaOf<O>
77
+ ): O extends unknown[] ? never : {} extends O ? never : Guard<O> {
78
+ if (schema == null || typeof schema !== "object") {
79
+ throw new TypeError(
80
+ `isObjectOf expects a guard schema object. Got instead: ${schema}`
81
+ );
82
+ }
83
+
84
+ const schemaKeys = objectKeys(schema);
85
+ if (
86
+ Array.isArray(schema) ||
87
+ schemaKeys.some((key) => typeof schema[key] !== "function")
88
+ ) {
89
+ throw new TypeError(
90
+ `isObjectOf expects a guard schema object. Got instead ${JSON.stringify(
91
+ schema
92
+ )}`
93
+ );
94
+ } else if (schemaKeys.length === 0) {
95
+ throw new Error("isObjectOf received an empty schema");
96
+ }
97
+
98
+ return ((value): value is O =>
99
+ value != null &&
100
+ typeof value === "object" &&
101
+ !Array.isArray(value) &&
102
+ schemaKeys.every(
103
+ (key) => key in value && schema[key]((value as O)[key])
104
+ )) as O extends unknown[] ? never : {} extends O ? never : Guard<O>;
105
+ }
@@ -1,136 +1,152 @@
1
- import {
2
- isExact,
3
- isNot,
4
- isNullable,
5
- isNonNullable,
6
- isOneOf,
7
- isOptional,
8
- isUnionOf,
9
- } from "../src/compound";
10
- import { isNumber, isString } from "../src/primitives";
11
-
12
- describe("isOptional", () => {
13
- const guard = isOptional(isString);
14
-
15
- it("succeeds for the expected type or undefined", () => {
16
- expect(guard("foo")).toBe(true);
17
- expect(guard(undefined)).toBe(true);
18
- });
19
-
20
- it("fails for any other value", () => {
21
- expect(guard(null)).toBe(false);
22
- });
23
- });
24
-
25
- describe("isNullable", () => {
26
- const guard = isNullable(isString);
27
-
28
- it("succeeds for the expected type, null, or undefined", () => {
29
- expect(guard("foo")).toBe(true);
30
- expect(guard(null)).toBe(true);
31
- expect(guard(undefined)).toBe(true);
32
- });
33
-
34
- it("fails any other value", () => {
35
- expect(guard(1)).toBe(false);
36
- });
37
- });
38
-
39
- describe("isNonNullable", () => {
40
- const guard = isNonNullable;
41
-
42
- it("succeeds for the expected type, null, or undefined", () => {
43
- expect(guard("foo")).toBe(true);
44
- expect(guard(1)).toBe(true);
45
- });
46
-
47
- it("fails any other value", () => {
48
- expect(guard(null)).toBe(false);
49
- expect(guard(undefined)).toBe(false);
50
- });
51
- });
52
-
53
- describe("isNot", () => {
54
- const guard = isNot(isString);
55
-
56
- it("succeeds for any other value", () => {
57
- expect(guard(1)).toBe(true);
58
- });
59
-
60
- it("fails for the isNot type", () => {
61
- expect(guard("foo")).toBe(false);
62
- });
63
- });
64
-
65
- describe("isOneOf", () => {
66
- const guard = isOneOf(1, "foo", true);
67
-
68
- it("succeeds for all of the values", () => {
69
- expect(guard(1)).toBe(true);
70
- expect(guard("foo")).toBe(true);
71
- expect(guard(true)).toBe(true);
72
- });
73
-
74
- expect(guard(2)).toBe(false);
75
- it("fails any other value", () => {
76
- expect(guard(null)).toBe(false);
77
- expect(guard("bar")).toBe(false);
78
- expect(guard(false)).toBe(false);
79
- });
80
- });
81
-
82
- describe("isUnionOf", () => {
83
- const guard = isUnionOf(isString, isNumber);
84
- it("succeeds for the union types", () => {
85
- expect(guard(1)).toBe(true);
86
- expect(guard("foo")).toBe(true);
87
- });
88
-
89
- it("fails for any other type", () => {
90
- expect(guard(true)).toBe(false);
91
- expect(guard(null)).toBe(false);
92
- });
93
- });
94
-
95
- describe("isExact", () => {
96
- describe("deep", () => {
97
- const guard = isExact(
98
- { foo: "bar", hello: ["world", { key: "test" }] },
99
- true
100
- );
101
-
102
- it("succeeds for the exact value", () => {
103
- expect(guard({ foo: "bar", hello: ["world", { key: "test" }] })).toBe(
104
- true
105
- );
106
- });
107
-
108
- it("fails for any other value", () => {
109
- expect(guard({ foo: "baz", hello: ["world", { key: "test" }] })).toBe(
110
- false
111
- );
112
- expect(guard({ foo: "bar", hello: ["world", { key: "tester" }] })).toBe(
113
- false
114
- );
115
- expect(guard(1)).toBe(false);
116
- });
117
- });
118
-
119
- describe("shallow", () => {
120
- const guard = isExact("foo", false);
121
-
122
- it("succeeds for the exact value", () => {
123
- expect(guard("foo")).toBe(true);
124
- });
125
-
126
- it("fails for deep equality", () => {
127
- const guard = isExact(["foo", "bar", "baz", 1, 2, 3], false);
128
- expect(guard(["foo", "bar", "baz", 1, 2, 3])).toBe(false);
129
- });
130
-
131
- it("fails for any other value", () => {
132
- expect(guard("bar")).toBe(false);
133
- expect(guard(1)).toBe(false);
134
- });
135
- });
136
- });
1
+ import {
2
+ isExact,
3
+ isIntersectionOf,
4
+ isNonNullable,
5
+ isNot,
6
+ isNullable,
7
+ isNumber,
8
+ isOneOf,
9
+ isOptional,
10
+ isString,
11
+ isUnionOf,
12
+ } from "../src";
13
+
14
+ describe("isOptional", () => {
15
+ const guard = isOptional(isString);
16
+
17
+ it("succeeds for the expected type or undefined", () => {
18
+ expect(guard("foo")).toBe(true);
19
+ expect(guard(undefined)).toBe(true);
20
+ });
21
+
22
+ it("fails for any other value", () => {
23
+ expect(guard(null)).toBe(false);
24
+ });
25
+ });
26
+
27
+ describe("isNullable", () => {
28
+ const guard = isNullable(isString);
29
+
30
+ it("succeeds for the expected type, null, or undefined", () => {
31
+ expect(guard("foo")).toBe(true);
32
+ expect(guard(null)).toBe(true);
33
+ expect(guard(undefined)).toBe(true);
34
+ });
35
+
36
+ it("fails any other value", () => {
37
+ expect(guard(1)).toBe(false);
38
+ });
39
+ });
40
+
41
+ describe("isNonNullable", () => {
42
+ it("succeeds for the expected type, null, or undefined", () => {
43
+ expect(isNonNullable("foo")).toBe(true);
44
+ expect(isNonNullable(1)).toBe(true);
45
+ });
46
+
47
+ it("fails any other value", () => {
48
+ expect(isNonNullable(null)).toBe(false);
49
+ expect(isNonNullable(undefined)).toBe(false);
50
+ });
51
+ });
52
+
53
+ describe("isNot", () => {
54
+ const guard = isNot(isString);
55
+
56
+ it("succeeds for any other value", () => {
57
+ expect(guard(1)).toBe(true);
58
+ });
59
+
60
+ it("fails for the isNot type", () => {
61
+ expect(guard("foo")).toBe(false);
62
+ });
63
+ });
64
+
65
+ describe("isOneOf", () => {
66
+ const guard = isOneOf(1, "foo", true);
67
+
68
+ it("succeeds for all of the values", () => {
69
+ expect(guard(1)).toBe(true);
70
+ expect(guard("foo")).toBe(true);
71
+ expect(guard(true)).toBe(true);
72
+ });
73
+
74
+ expect(guard(2)).toBe(false);
75
+ it("fails any other value", () => {
76
+ expect(guard(null)).toBe(false);
77
+ expect(guard("bar")).toBe(false);
78
+ expect(guard(false)).toBe(false);
79
+ });
80
+ });
81
+
82
+ describe("isUnionOf", () => {
83
+ const guard = isUnionOf(isString, isNumber);
84
+ it("succeeds for the union types", () => {
85
+ expect(guard(1)).toBe(true);
86
+ expect(guard("foo")).toBe(true);
87
+ });
88
+
89
+ it("fails for any other type", () => {
90
+ expect(guard(true)).toBe(false);
91
+ expect(guard(null)).toBe(false);
92
+ });
93
+ });
94
+
95
+ describe("isIntersectionOf", () => {
96
+ const guard = isIntersectionOf(
97
+ isOneOf("foo", "bar", "baz"),
98
+ isExact("foo", false)
99
+ );
100
+
101
+ it("succeeds for the intersection", () => {
102
+ expect(guard("foo")).toBe(true);
103
+ });
104
+
105
+ it("fails for any other type", () => {
106
+ expect(guard("bar")).toBe(false);
107
+ expect(guard(1)).toBe(false);
108
+ });
109
+ });
110
+
111
+ describe("isExact", () => {
112
+ describe("deep", () => {
113
+ const guard = isExact(
114
+ { foo: "bar", hello: ["world", { key: "test" }] },
115
+ true
116
+ );
117
+
118
+ it("succeeds for the exact value", () => {
119
+ expect(guard({ foo: "bar", hello: ["world", { key: "test" }] })).toBe(
120
+ true
121
+ );
122
+ });
123
+
124
+ it("fails for any other value", () => {
125
+ expect(guard({ foo: "baz", hello: ["world", { key: "test" }] })).toBe(
126
+ false
127
+ );
128
+ expect(guard({ foo: "bar", hello: ["world", { key: "tester" }] })).toBe(
129
+ false
130
+ );
131
+ expect(guard(1)).toBe(false);
132
+ });
133
+ });
134
+
135
+ describe("shallow", () => {
136
+ const guard = isExact("foo", false);
137
+
138
+ it("succeeds for the exact value", () => {
139
+ expect(guard("foo")).toBe(true);
140
+ });
141
+
142
+ it("fails for deep equality", () => {
143
+ const guard = isExact(["foo", "bar", "baz", 1, 2, 3], false);
144
+ expect(guard(["foo", "bar", "baz", 1, 2, 3])).toBe(false);
145
+ });
146
+
147
+ it("fails for any other value", () => {
148
+ expect(guard("bar")).toBe(false);
149
+ expect(guard(1)).toBe(false);
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,15 @@
1
+ import { isDiscriminatedObjectOf, isObjectOf, isString } from "../src";
2
+
3
+ describe("isDiscriminatedObjectOf", () => {
4
+ const guard = isDiscriminatedObjectOf("foo", isObjectOf({ bar: isString }));
5
+
6
+ it("succeeds for an object of the value", () => {
7
+ expect(guard({ type: "foo", bar: "baz" })).toBe(true);
8
+ });
9
+
10
+ it("fails for any other value", () => {
11
+ expect(guard({ type: "foo" })).toBe(false);
12
+ expect(guard({ bar: "baz" })).toBe(false);
13
+ expect(guard(1)).toBe(false);
14
+ });
15
+ });