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 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. [isExact](#isexact)
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. [guardOrThrow](#guardorthrow)
63
- 5. [TypeFromGuard](#typefromguard)
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
@@ -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.1",
11
+ "version": "1.0.2",
12
12
  "scripts": {
13
13
  "test": "jest",
14
14
  "build": "tsc",
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
- function isEqual<T>(a: T, b: unknown): b is T {
61
- return (
62
- a === b ||
63
- (a != null &&
64
- b != null &&
65
- typeof a === "object" &&
66
- typeof b === "object" &&
67
- (Array.isArray(a)
68
- ? Array.isArray(b) &&
69
- a.length === b.length &&
70
- a.every((v, i) => isEqual(v, b[i]))
71
- : Object.keys(a).length === Object.keys(b).length &&
72
- Object.entries(a).every(
73
- ([k, v]) => k in b && isEqual(v, (b as Record<string, unknown>)[k])
74
- )))
75
- );
76
- }
77
-
78
- export function isExact<const T>(expected: T, deep: boolean = true): Guard<T> {
79
- return (value): value is T =>
80
- deep ? isEqual(expected, value) : expected === value;
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 (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.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
+ }
@@ -1,136 +1,151 @@
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
+ 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
+ });