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/README.md +79 -6
- package/dist/compound.d.ts +7 -1
- package/dist/compound.js +7 -1
- package/dist/compound.js.map +1 -1
- package/dist/errors.d.ts +1 -1
- 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 +4 -0
- package/dist/macros.js +4 -0
- package/dist/macros.js.map +1 -0
- package/dist/primitives.d.ts +1 -1
- package/dist/structures.d.ts +3 -2
- package/dist/structures.js +16 -8
- package/dist/structures.js.map +1 -1
- package/package.json +5 -5
- package/src/compound.ts +99 -81
- package/src/errors.ts +17 -17
- package/src/index.ts +6 -5
- package/src/macros.ts +11 -0
- package/src/primitives.ts +23 -23
- package/src/structures.ts +105 -83
- package/tests/compound.test.ts +152 -136
- package/tests/macros.test.ts +15 -0
- package/tests/primitives.test.ts +91 -91
- package/tests/structures.test.ts +128 -110
package/src/structures.ts
CHANGED
|
@@ -1,83 +1,105 @@
|
|
|
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
|
|
29
|
-
|
|
30
|
-
): Guard<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
);
|
|
43
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/tests/compound.test.ts
CHANGED
|
@@ -1,136 +1,152 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isExact,
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
expect(guard(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
it("succeeds for the expected type, null, or undefined", () => {
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it("fails any other value", () => {
|
|
48
|
-
expect(
|
|
49
|
-
expect(
|
|
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("
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
expect(guard(
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
+
});
|