deep-guards 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -4
- package/package.json +1 -1
- package/src/compound.ts +101 -81
- package/src/macros.ts +11 -0
- package/src/structures.ts +83 -83
- package/tests/compound.test.ts +151 -136
package/README.md
CHANGED
|
@@ -52,15 +52,18 @@ if (vehicleGuard(value)) {
|
|
|
52
52
|
4. [isNot](#isnot)
|
|
53
53
|
5. [isOneOf](#isoneof)
|
|
54
54
|
6. [isUnionOf](#isunionof)
|
|
55
|
-
7. [
|
|
55
|
+
7. [isIntersectionOf](#isintersectionof)
|
|
56
|
+
8. [isExact](#isexact)
|
|
56
57
|
3. [Structures](#structures)
|
|
57
58
|
1. [isAnyArray](#isanyarray)
|
|
58
59
|
2. [isAnyRecord](#isanyrecord)
|
|
59
60
|
3. [isArrayOf](#isarrayof)
|
|
60
61
|
4. [isRecordOf](#isrecordof)
|
|
61
62
|
5. [isObjectOf](#isobjectof)
|
|
62
|
-
4. [
|
|
63
|
-
|
|
63
|
+
4. [Macros](#macros)
|
|
64
|
+
1. [isDiscriminatedObjectOf](#isdiscriminatedobjectof)
|
|
65
|
+
5. [guardOrThrow](#guardorthrow)
|
|
66
|
+
6. [TypeFromGuard](#typefromguard)
|
|
64
67
|
|
|
65
68
|
## Terminology
|
|
66
69
|
|
|
@@ -123,7 +126,11 @@ It's very useful for enumerations, where you only have a few specific values, e.
|
|
|
123
126
|
|
|
124
127
|
### isUnionOf
|
|
125
128
|
|
|
126
|
-
Higher order guard. This takes in any amount of guards as arguments, and then produces a guard which does a union over all the incoming guards.
|
|
129
|
+
Higher order guard. This takes in any amount of guards as arguments, and then produces a guard which does a union over all the incoming guards. This means that if _any_ one of the guards passes for the incoming value, then this will pass.
|
|
130
|
+
|
|
131
|
+
### isIntersectionOf
|
|
132
|
+
|
|
133
|
+
Higher order guard. This takes in any amount of guards as arguments, and then produces a guard which does an intersection over all the incoming guards. This means that if _every_ one of the guards passes for the incoming value, then this will pass.
|
|
127
134
|
|
|
128
135
|
### isExact
|
|
129
136
|
|
|
@@ -168,6 +175,56 @@ As seen in the example at the start of this readme, you can do all sorts of comp
|
|
|
168
175
|
NOTE: This throws an error if you give it an empty object.\
|
|
169
176
|
It will also accept an object which contains keys which are not specified.
|
|
170
177
|
|
|
178
|
+
## Macros
|
|
179
|
+
|
|
180
|
+
These are common use cases for guarding setups, where they are made entirely out of the above guard suite.
|
|
181
|
+
|
|
182
|
+
### isDiscriminatedObjectOf
|
|
183
|
+
|
|
184
|
+
This takes in a string literal `type`, and an `isObjectOf` guard. This then combines the two, where the returned guard has the signature: `Guard<{ type: T } & O>`.
|
|
185
|
+
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
|
+
|
|
187
|
+
```ts
|
|
188
|
+
interface Car {
|
|
189
|
+
wheels: 4;
|
|
190
|
+
owner: string;
|
|
191
|
+
passengers: {
|
|
192
|
+
name: string;
|
|
193
|
+
}[];
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
interface Bike {
|
|
197
|
+
wheels: 2;
|
|
198
|
+
owner: string;
|
|
199
|
+
storage?: string[];
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
type Vehicle = ({ type: "car" } & Car) | ({ type: "bike" } & Bike);
|
|
203
|
+
|
|
204
|
+
// Can then be represented like so in guards:
|
|
205
|
+
|
|
206
|
+
const carGuard = isObjectOf({
|
|
207
|
+
wheels: isExact(4),
|
|
208
|
+
owner: isString,
|
|
209
|
+
passengers: isArrayOf(
|
|
210
|
+
isObjectOf({
|
|
211
|
+
name: isString,
|
|
212
|
+
})
|
|
213
|
+
),
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const bikeGuard = isObjectOf({
|
|
217
|
+
wheels: isExact(2),
|
|
218
|
+
owner: isString,
|
|
219
|
+
storage: isOptional(isArrayOf(isString)),
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const vehicleGuard = isUnionOf(
|
|
223
|
+
isDiscriminatedObjectOf("car", carGuard),
|
|
224
|
+
isDiscriminatedObjectOf("bike", bikeGuard)
|
|
225
|
+
);
|
|
226
|
+
```
|
|
227
|
+
|
|
171
228
|
## guardOrThrow
|
|
172
229
|
|
|
173
230
|
This package also includes a `guardOrThrow` method which when given an incoming value, a guard, and an optional hint message, will return a narrowed version of the value, or throw a `GuardError` containing that hint message.
|
package/package.json
CHANGED
package/src/compound.ts
CHANGED
|
@@ -1,81 +1,101 @@
|
|
|
1
|
-
import { Guard, GuardSchemaOf } from "./types.js";
|
|
2
|
-
|
|
3
|
-
export function isOptional<T>(guard: Guard<T>): Guard<T | undefined> {
|
|
4
|
-
if (typeof guard !== "function") {
|
|
5
|
-
throw new TypeError(
|
|
6
|
-
`isOptional expects a guard parameter. Got instead: ${guard}`
|
|
7
|
-
);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
return ((value) => value === undefined || guard(value)) as Guard<
|
|
11
|
-
T | undefined
|
|
12
|
-
>;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export function isNullable<T>(guard: Guard<T>): Guard<T | null | undefined> {
|
|
16
|
-
if (typeof guard !== "function") {
|
|
17
|
-
throw new TypeError(
|
|
18
|
-
`isNullable expects a guard parameter. Got instead: ${guard}`
|
|
19
|
-
);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return (value: unknown): value is T | null | undefined =>
|
|
23
|
-
value == null || guard(value);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export function isNonNullable<T>(value: T | null | undefined): value is T {
|
|
27
|
-
return value != null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function isNot<const N>(guard: Guard<N>) {
|
|
31
|
-
if (typeof guard !== "function") {
|
|
32
|
-
throw new TypeError(
|
|
33
|
-
`isNot expects a guard parameter. Got instead: ${guard}`
|
|
34
|
-
);
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
return <const T>(value: T | N): value is T => !guard(value);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function isOneOf<
|
|
41
|
-
const T extends (string | number | boolean | symbol | null | undefined)[]
|
|
42
|
-
>(...values: T): Guard<(typeof values)[number]> {
|
|
43
|
-
const valueSet = new Set(values);
|
|
44
|
-
return (value: unknown): value is T[number] =>
|
|
45
|
-
valueSet.has(value as T[number]);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function isUnionOf<T extends readonly unknown[]>(
|
|
49
|
-
...guards: GuardSchemaOf<T>
|
|
50
|
-
): Guard<T[number]> {
|
|
51
|
-
if (guards.every((guard) => typeof guard !== "function")) {
|
|
52
|
-
throw new TypeError(
|
|
53
|
-
`isUnionOf expects N guard parameters. Got instead: ${guards}`
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return (value): value is T => guards.some((guard) => guard(value));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1
|
+
import { Guard, GuardSchemaOf } from "./types.js";
|
|
2
|
+
|
|
3
|
+
export function isOptional<T>(guard: Guard<T>): Guard<T | undefined> {
|
|
4
|
+
if (typeof guard !== "function") {
|
|
5
|
+
throw new TypeError(
|
|
6
|
+
`isOptional expects a guard parameter. Got instead: ${guard}`
|
|
7
|
+
);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
return ((value) => value === undefined || guard(value)) as Guard<
|
|
11
|
+
T | undefined
|
|
12
|
+
>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function isNullable<T>(guard: Guard<T>): Guard<T | null | undefined> {
|
|
16
|
+
if (typeof guard !== "function") {
|
|
17
|
+
throw new TypeError(
|
|
18
|
+
`isNullable expects a guard parameter. Got instead: ${guard}`
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (value: unknown): value is T | null | undefined =>
|
|
23
|
+
value == null || guard(value);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function isNonNullable<T>(value: T | null | undefined): value is T {
|
|
27
|
+
return value != null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isNot<const N>(guard: Guard<N>) {
|
|
31
|
+
if (typeof guard !== "function") {
|
|
32
|
+
throw new TypeError(
|
|
33
|
+
`isNot expects a guard parameter. Got instead: ${guard}`
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return <const T>(value: T | N): value is T => !guard(value);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function isOneOf<
|
|
41
|
+
const T extends (string | number | boolean | symbol | null | undefined)[]
|
|
42
|
+
>(...values: T): Guard<(typeof values)[number]> {
|
|
43
|
+
const valueSet = new Set(values);
|
|
44
|
+
return (value: unknown): value is T[number] =>
|
|
45
|
+
valueSet.has(value as T[number]);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function isUnionOf<T extends readonly unknown[]>(
|
|
49
|
+
...guards: GuardSchemaOf<T>
|
|
50
|
+
): Guard<T[number]> {
|
|
51
|
+
if (guards.every((guard) => typeof guard !== "function")) {
|
|
52
|
+
throw new TypeError(
|
|
53
|
+
`isUnionOf expects N guard parameters. Got instead: ${guards}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (value): value is T => guards.some((guard) => guard(value));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
type ArrayToIntersection<A extends readonly unknown[]> = A extends [
|
|
61
|
+
infer T,
|
|
62
|
+
...infer R
|
|
63
|
+
]
|
|
64
|
+
? T & ArrayToIntersection<R>
|
|
65
|
+
: unknown;
|
|
66
|
+
|
|
67
|
+
export function isIntersectionOf<T extends readonly unknown[]>(
|
|
68
|
+
...guards: GuardSchemaOf<T>
|
|
69
|
+
): Guard<ArrayToIntersection<T>> {
|
|
70
|
+
if (guards.every((guard) => typeof guard !== "function")) {
|
|
71
|
+
throw new TypeError(
|
|
72
|
+
`isIntersectionOf expects N guard parameters. Got instead: ${guards}`
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (value): value is ArrayToIntersection<T> =>
|
|
77
|
+
guards.every((guard) => guard(value));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function isEqual<T>(a: T, b: unknown): b is T {
|
|
81
|
+
return (
|
|
82
|
+
a === b ||
|
|
83
|
+
(a != null &&
|
|
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
|
+
)))
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function isExact<const T>(expected: T, deep: boolean = true): Guard<T> {
|
|
99
|
+
return (value): value is T =>
|
|
100
|
+
deep ? isEqual(expected, value) : expected === value;
|
|
101
|
+
}
|
package/src/macros.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isExact, isIntersectionOf } from "./compound";
|
|
2
|
+
import { isObjectOf } from "./structures";
|
|
3
|
+
import { Guard } from "./types";
|
|
4
|
+
|
|
5
|
+
export const isDiscriminatedObjectOf = <T extends string, O extends object>(
|
|
6
|
+
type: T,
|
|
7
|
+
guard: Guard<O>
|
|
8
|
+
): Guard<{ type: T } & O> =>
|
|
9
|
+
isIntersectionOf(isObjectOf({ type: isExact(type) }), guard) as Guard<
|
|
10
|
+
{ type: T } & O
|
|
11
|
+
>;
|
package/src/structures.ts
CHANGED
|
@@ -1,83 +1,83 @@
|
|
|
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 (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
schemaUnknown
|
|
67
|
-
typeof schemaUnknown !== "
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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.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 (
|
|
64
|
+
schemaUnknown == null ||
|
|
65
|
+
typeof schemaUnknown !== "object" ||
|
|
66
|
+
Array.isArray(schemaUnknown) ||
|
|
67
|
+
schemaKeys.some((key) => typeof (schemaUnknown as O)[key] !== "function")
|
|
68
|
+
) {
|
|
69
|
+
throw new TypeError(
|
|
70
|
+
`isObjectOf expects a guard schema. Got instead: ${schemaUnknown}`
|
|
71
|
+
);
|
|
72
|
+
} else if (schemaKeys.length === 0) {
|
|
73
|
+
throw new Error("isObjectOf received an empty schema");
|
|
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
|
+
}
|
package/tests/compound.test.ts
CHANGED
|
@@ -1,136 +1,151 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isExact,
|
|
3
|
-
isNot,
|
|
4
|
-
isNullable,
|
|
5
|
-
isNonNullable,
|
|
6
|
-
isOneOf,
|
|
7
|
-
isOptional,
|
|
8
|
-
isUnionOf,
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
expect(guard(
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
expect(guard(
|
|
31
|
-
expect(guard(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
expect(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
expect(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
expect(guard(
|
|
70
|
-
expect(guard(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
expect(guard(
|
|
77
|
-
expect(guard(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
expect(guard(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
expect(guard(
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
1
|
+
import {
|
|
2
|
+
isExact,
|
|
3
|
+
isNot,
|
|
4
|
+
isNullable,
|
|
5
|
+
isNonNullable,
|
|
6
|
+
isOneOf,
|
|
7
|
+
isOptional,
|
|
8
|
+
isUnionOf,
|
|
9
|
+
isIntersectionOf,
|
|
10
|
+
} from "../src/compound";
|
|
11
|
+
import { isNumber, isString } from "../src/primitives";
|
|
12
|
+
|
|
13
|
+
describe("isOptional", () => {
|
|
14
|
+
const guard = isOptional(isString);
|
|
15
|
+
|
|
16
|
+
it("succeeds for the expected type or undefined", () => {
|
|
17
|
+
expect(guard("foo")).toBe(true);
|
|
18
|
+
expect(guard(undefined)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it("fails for any other value", () => {
|
|
22
|
+
expect(guard(null)).toBe(false);
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("isNullable", () => {
|
|
27
|
+
const guard = isNullable(isString);
|
|
28
|
+
|
|
29
|
+
it("succeeds for the expected type, null, or undefined", () => {
|
|
30
|
+
expect(guard("foo")).toBe(true);
|
|
31
|
+
expect(guard(null)).toBe(true);
|
|
32
|
+
expect(guard(undefined)).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("fails any other value", () => {
|
|
36
|
+
expect(guard(1)).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe("isNonNullable", () => {
|
|
41
|
+
it("succeeds for the expected type, null, or undefined", () => {
|
|
42
|
+
expect(isNonNullable("foo")).toBe(true);
|
|
43
|
+
expect(isNonNullable(1)).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("fails any other value", () => {
|
|
47
|
+
expect(isNonNullable(null)).toBe(false);
|
|
48
|
+
expect(isNonNullable(undefined)).toBe(false);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe("isNot", () => {
|
|
53
|
+
const guard = isNot(isString);
|
|
54
|
+
|
|
55
|
+
it("succeeds for any other value", () => {
|
|
56
|
+
expect(guard(1)).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("fails for the isNot type", () => {
|
|
60
|
+
expect(guard("foo")).toBe(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("isOneOf", () => {
|
|
65
|
+
const guard = isOneOf(1, "foo", true);
|
|
66
|
+
|
|
67
|
+
it("succeeds for all of the values", () => {
|
|
68
|
+
expect(guard(1)).toBe(true);
|
|
69
|
+
expect(guard("foo")).toBe(true);
|
|
70
|
+
expect(guard(true)).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(guard(2)).toBe(false);
|
|
74
|
+
it("fails any other value", () => {
|
|
75
|
+
expect(guard(null)).toBe(false);
|
|
76
|
+
expect(guard("bar")).toBe(false);
|
|
77
|
+
expect(guard(false)).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("isUnionOf", () => {
|
|
82
|
+
const guard = isUnionOf(isString, isNumber);
|
|
83
|
+
it("succeeds for the union types", () => {
|
|
84
|
+
expect(guard(1)).toBe(true);
|
|
85
|
+
expect(guard("foo")).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("fails for any other type", () => {
|
|
89
|
+
expect(guard(true)).toBe(false);
|
|
90
|
+
expect(guard(null)).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe("isIntersectionOf", () => {
|
|
95
|
+
const guard = isIntersectionOf(
|
|
96
|
+
isOneOf("foo", "bar", "baz"),
|
|
97
|
+
isExact("foo", false)
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
it("succeeds for the intersection", () => {
|
|
101
|
+
expect(guard("foo")).toBe(true);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("fails for any other type", () => {
|
|
105
|
+
expect(guard("bar")).toBe(false);
|
|
106
|
+
expect(guard(1)).toBe(false);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe("isExact", () => {
|
|
111
|
+
describe("deep", () => {
|
|
112
|
+
const guard = isExact(
|
|
113
|
+
{ foo: "bar", hello: ["world", { key: "test" }] },
|
|
114
|
+
true
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
it("succeeds for the exact value", () => {
|
|
118
|
+
expect(guard({ foo: "bar", hello: ["world", { key: "test" }] })).toBe(
|
|
119
|
+
true
|
|
120
|
+
);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("fails for any other value", () => {
|
|
124
|
+
expect(guard({ foo: "baz", hello: ["world", { key: "test" }] })).toBe(
|
|
125
|
+
false
|
|
126
|
+
);
|
|
127
|
+
expect(guard({ foo: "bar", hello: ["world", { key: "tester" }] })).toBe(
|
|
128
|
+
false
|
|
129
|
+
);
|
|
130
|
+
expect(guard(1)).toBe(false);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
describe("shallow", () => {
|
|
135
|
+
const guard = isExact("foo", false);
|
|
136
|
+
|
|
137
|
+
it("succeeds for the exact value", () => {
|
|
138
|
+
expect(guard("foo")).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("fails for deep equality", () => {
|
|
142
|
+
const guard = isExact(["foo", "bar", "baz", 1, 2, 3], false);
|
|
143
|
+
expect(guard(["foo", "bar", "baz", 1, 2, 3])).toBe(false);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("fails for any other value", () => {
|
|
147
|
+
expect(guard("bar")).toBe(false);
|
|
148
|
+
expect(guard(1)).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
});
|