deep-guards 1.2.0 → 1.3.0

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
@@ -54,6 +54,7 @@ if (vehicleGuard(value)) {
54
54
  6. [isUnionOf](#isunionof)
55
55
  7. [isIntersectionOf](#isintersectionof)
56
56
  8. [isExact](#isexact)
57
+ 9. [isInstance](#isinstance)
57
58
  3. [Structures](#structures)
58
59
  1. [isAnyArray](#isanyarray)
59
60
  2. [isAnyRecord](#isanyrecord)
@@ -119,7 +120,7 @@ Has signature:
119
120
 
120
121
  ```ts
121
122
  function isOneOf<
122
- const T extends (string | number | boolean | symbol | null | undefined)[]
123
+ const T extends (string | number | boolean | symbol | null | undefined)[],
123
124
  >(...values: T): Guard<(typeof values)[number]>;
124
125
  ```
125
126
 
@@ -138,10 +139,14 @@ Higher order guard. This takes in any amount of guards as arguments, and then pr
138
139
  Has signature:
139
140
 
140
141
  ```ts
141
- function isExact<const T>(expected: T, deep: boolean = true): Guard<T>;
142
+ function isExact<const T>(expected: T): Guard<T>;
142
143
  ```
143
144
 
144
- This will pass if the incoming value exactly matches the `expected` parameter, optionally computing a deep equality.
145
+ This will pass if the incoming value exactly matches the `expected` parameter. This memoizes checks upfront based on the expected value being passed in. So it's best to create these guards once, and then reuse the returned guard function.
146
+
147
+ ### isInstance
148
+
149
+ Takes in a class and produces a guard to see if values are instances of that class, by using `instanceof`
145
150
 
146
151
  ## Structures
147
152
 
@@ -228,7 +233,7 @@ const carGuard = isObjectOf({
228
233
  passengers: isArrayOf(
229
234
  isObjectOf({
230
235
  name: isString,
231
- })
236
+ }),
232
237
  ),
233
238
  });
234
239
 
@@ -240,7 +245,7 @@ const bikeGuard = isObjectOf({
240
245
 
241
246
  const vehicleGuard = isUnionOf(
242
247
  isDiscriminatedObjectOf("car", carGuard),
243
- isDiscriminatedObjectOf("bike", bikeGuard)
248
+ isDiscriminatedObjectOf("bike", bikeGuard),
244
249
  );
245
250
  ```
246
251
 
@@ -254,7 +259,7 @@ You can then do things like:
254
259
  const cars = guardOrThrow(
255
260
  JSON.parse(readFileSync("cars.json").toString()),
256
261
  isArrayOf(isCar),
257
- "Invalid car format"
262
+ "Invalid car format",
258
263
  );
259
264
  ```
260
265
 
@@ -272,7 +277,7 @@ const carGuard = isObjectOf({
272
277
  passengers: isArrayOf(
273
278
  isObjectOf({
274
279
  name: isString,
275
- })
280
+ }),
276
281
  ),
277
282
  });
278
283
 
@@ -11,6 +11,6 @@ type ArrayToIntersection<A extends readonly unknown[]> = A extends [
11
11
  ...infer R
12
12
  ] ? T & ArrayToIntersection<R> : unknown;
13
13
  export declare function isIntersectionOf<T extends readonly unknown[]>(...guards: GuardSchemaOf<T>): Guard<ArrayToIntersection<T>>;
14
- export declare function isExact<const T>(expected: T, deep?: boolean): Guard<T>;
14
+ export declare function isExact<const T>(expected: T): Guard<T>;
15
15
  export declare function isInstance<C extends abstract new (...args: any) => unknown>(cls: C): Guard<InstanceType<C>>;
16
16
  export {};
package/dist/compound.js CHANGED
@@ -36,30 +36,76 @@ export function isIntersectionOf(...guards) {
36
36
  }
37
37
  return (value) => guards.every((guard) => guard(value));
38
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
- }
45
- export function isExact(expected, deep = true) {
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)));
39
+ export function isExact(expected) {
40
+ // Primitive checks
41
+ if (typeof expected !== "object" || expected === null) {
42
+ // NaN check
43
+ if (typeof expected === "number" && Number.isNaN(expected)) {
44
+ return (value) => typeof value === "number" && Number.isNaN(value);
45
+ }
46
+ return (value) => value === expected;
47
+ }
48
+ // Date checks
49
+ if (expected instanceof Date) {
50
+ return (value) => value instanceof Date && expected.getTime() === value.getTime();
51
+ }
52
+ // RegExp checks
53
+ if (expected instanceof RegExp) {
54
+ return (value) => value instanceof RegExp &&
55
+ expected.source === value.source &&
56
+ expected.flags === value.flags;
57
+ }
58
+ // Error checks
59
+ if (expected instanceof Error) {
60
+ return (value) => value instanceof Error &&
61
+ expected.name === value.name &&
62
+ expected.message === value.message;
63
+ }
64
+ // Map checks
65
+ if (expected instanceof Map) {
66
+ const guards = Array.from(expected
67
+ .entries()
68
+ .map(([k, v]) => [k, isExact(v)]));
69
+ return (value) => value instanceof Map &&
70
+ expected.size === value.size &&
71
+ guards.every(([k, guard]) => value.has(k) && guard(value.get(k)));
72
+ }
73
+ // Set checks
74
+ if (expected instanceof Set) {
75
+ const guards = new Map(expected.values().map((v) => [v, isExact(v)]));
76
+ return (value) => value instanceof Set &&
77
+ expected.size === value.size &&
78
+ value.values().every((v) => guards.get(v)?.(v));
79
+ }
80
+ // Typed Array checks
81
+ if (ArrayBuffer.isView(expected) && !(expected instanceof DataView)) {
82
+ const guards = Array.from(expected).map((v) => isExact(v));
83
+ return (value) => ArrayBuffer.isView(value) &&
84
+ !(value instanceof DataView) &&
85
+ expected.constructor === value.constructor &&
86
+ expected.length ===
87
+ value.length &&
88
+ guards.every((guard, i) => guard(value[i]));
89
+ }
90
+ // Array checks
91
+ if (Array.isArray(expected)) {
92
+ const guards = expected.map((v) => isExact(v));
93
+ return (value) => Array.isArray(value) &&
94
+ expected.length === value.length &&
95
+ guards.every((guard, i) => guard(value[i]));
96
+ }
97
+ // Object checks
98
+ const guards = objectKeys(expected).map((k) => [k, isExact(expected[k])]);
99
+ function objectEntriesChecks(value) {
100
+ const valueKeys = new Set(objectKeys(value));
101
+ return (guards.length === valueKeys.size &&
102
+ guards.every(([k, guard]) => valueKeys.has(k) && guard(value[k])));
103
+ }
104
+ return (value) => typeof value === "object" &&
105
+ value != null &&
106
+ !Array.isArray(value) &&
107
+ objectKeys(value).length === guards.length &&
108
+ objectEntriesChecks(value);
63
109
  }
64
110
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
65
111
  export function isInstance(cls) {
@@ -1 +1 @@
1
- {"version":3,"file":"compound.js","sourceRoot":"src/","sources":["compound.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compound.js","sourceRoot":"src/","sources":["compound.ts"],"names":[],"mappings":"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"}
package/eslint.config.js CHANGED
@@ -28,8 +28,10 @@ export default tslint.config(
28
28
  "@typescript-eslint/no-unnecessary-condition": "error",
29
29
  "@stylistic/arrow-parens": "off",
30
30
  "@stylistic/indent": "off",
31
+ "@stylistic/indent-binary-ops": "off",
31
32
  "@stylistic/generator-star-spacing": "off",
32
33
  "@stylistic/quotes": "off",
34
+ "@stylistic/quote-props": "off",
33
35
  "@stylistic/operator-linebreak": "off",
34
36
 
35
37
  // CORE
@@ -62,5 +64,5 @@ export default tslint.config(
62
64
  { varsIgnorePattern: "^_", argsIgnorePattern: "^_" },
63
65
  ],
64
66
  },
65
- }
67
+ },
66
68
  );
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "type": "module",
12
12
  "types": "dist/index.d.ts",
13
13
  "homepage": "https://github.com/eniallator/Deep-Guards",
14
- "version": "1.2.0",
14
+ "version": "1.3.0",
15
15
  "scripts": {
16
16
  "test": "vitest run --passWithNoTests",
17
17
  "build": "tsc",
@@ -95,10 +95,7 @@ describe("isUnionOf", () => {
95
95
  });
96
96
 
97
97
  describe("isIntersectionOf", () => {
98
- const guard = isIntersectionOf(
99
- isOneOf("foo", "bar", "baz"),
100
- isExact("foo", false),
101
- );
98
+ const guard = isIntersectionOf(isOneOf("foo", "bar", "baz"), isExact("foo"));
102
99
 
103
100
  it("succeeds for the intersection", () => {
104
101
  expect(guard("foo")).toBe(true);
@@ -112,10 +109,7 @@ describe("isIntersectionOf", () => {
112
109
 
113
110
  describe("isExact", () => {
114
111
  describe("deep", () => {
115
- const guard = isExact(
116
- { foo: "bar", hello: ["world", { key: "test" }] },
117
- true,
118
- );
112
+ const guard = isExact({ foo: "bar", hello: ["world", { key: "test" }] });
119
113
 
120
114
  it("succeeds for the exact value", () => {
121
115
  expect(guard({ foo: "bar", hello: ["world", { key: "test" }] })).toBe(
@@ -135,22 +129,108 @@ describe("isExact", () => {
135
129
  });
136
130
 
137
131
  describe("shallow", () => {
138
- const guard = isExact("foo", false);
132
+ const guard = isExact("foo");
139
133
 
140
134
  it("succeeds for the exact value", () => {
141
135
  expect(guard("foo")).toBe(true);
142
136
  });
143
137
 
144
- it("fails for deep equality", () => {
145
- const guard = isExact(["foo", "bar", "baz", 1, 2, 3], false);
146
- expect(guard(["foo", "bar", "baz", 1, 2, 3])).toBe(false);
147
- });
148
-
149
138
  it("fails for any other value", () => {
150
139
  expect(guard("bar")).toBe(false);
151
140
  expect(guard(1)).toBe(false);
152
141
  });
153
142
  });
143
+
144
+ describe("Date", () => {
145
+ const date = new Date("2000-01-01");
146
+ const guard = isExact(date);
147
+
148
+ it("succeeds for the exact same date", () => {
149
+ expect(guard(new Date("2000-01-01"))).toBe(true);
150
+ });
151
+
152
+ it("fails for different dates", () => {
153
+ expect(guard(new Date("2000-01-02"))).toBe(false);
154
+ expect(guard(date.getTime())).toBe(false);
155
+ });
156
+ });
157
+
158
+ describe("RegExp", () => {
159
+ const regex = /test/gi;
160
+ const guard = isExact(regex);
161
+
162
+ it("succeeds for the exact same regex", () => {
163
+ expect(guard(/test/gi)).toBe(true);
164
+ });
165
+
166
+ it("fails for different regexes", () => {
167
+ expect(guard(/test/i)).toBe(false);
168
+ expect(guard(/test2/gi)).toBe(false);
169
+ expect(guard("test")).toBe(false);
170
+ });
171
+ });
172
+
173
+ describe("Error", () => {
174
+ const error = new Error("test error");
175
+ const guard = isExact(error);
176
+
177
+ it("succeeds for the exact same error", () => {
178
+ expect(guard(new Error("test error"))).toBe(true);
179
+ });
180
+
181
+ it("fails for different errors", () => {
182
+ expect(guard(new Error("other error"))).toBe(false);
183
+ expect(guard(new TypeError("test error"))).toBe(false);
184
+ expect(guard("test error")).toBe(false);
185
+ });
186
+ });
187
+
188
+ describe("Map", () => {
189
+ const map = new Map([["key", "value"]]);
190
+ const guard = isExact(map);
191
+
192
+ it("succeeds for the exact same map", () => {
193
+ expect(guard(new Map([["key", "value"]]))).toBe(true);
194
+ });
195
+
196
+ it("fails for different maps", () => {
197
+ expect(guard(new Map([["key", "other"]]))).toBe(false);
198
+ expect(guard(new Map([["other", "value"]]))).toBe(false);
199
+ expect(guard(new Map())).toBe(false);
200
+ expect(guard({})).toBe(false);
201
+ });
202
+ });
203
+
204
+ describe("Set", () => {
205
+ const set = new Set([1, 2, 3]);
206
+ const guard = isExact(set);
207
+
208
+ it("succeeds for the exact same set", () => {
209
+ expect(guard(new Set([1, 2, 3]))).toBe(true);
210
+ });
211
+
212
+ it("fails for different sets", () => {
213
+ expect(guard(new Set([1, 2]))).toBe(false);
214
+ expect(guard(new Set([1, 2, 4]))).toBe(false);
215
+ expect(guard([1, 2, 3])).toBe(false);
216
+ });
217
+ });
218
+
219
+ describe("Typed Array", () => {
220
+ const array = new Uint8Array([1, 2, 3]);
221
+ const guard = isExact(array);
222
+
223
+ it("succeeds for the exact same typed array", () => {
224
+ expect(guard(new Uint8Array([1, 2, 3]))).toBe(true);
225
+ });
226
+
227
+ it("fails for different typed arrays", () => {
228
+ expect(guard(new Uint8Array([1, 2]))).toBe(false);
229
+ expect(guard(new Uint8Array([1, 2, 4]))).toBe(false);
230
+ expect(guard(new Int8Array([1, 2, 3]))).toBe(false);
231
+ expect(guard([1, 2, 3])).toBe(false);
232
+ });
233
+ });
154
234
  });
155
235
 
156
236
  describe("isInstance", () => {
package/src/compound.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { objectKeys } from "./helpers.js";
2
2
 
3
- import type { GuardSchemaOf } from "./helpers.js";
3
+ import type { GuardSchemaOf, ObjectKey } from "./helpers.js";
4
4
  import type { Guard } from "./types.js";
5
5
 
6
6
  export function isOptional<T>(guard: Guard<T>): Guard<T | undefined> {
@@ -79,33 +79,114 @@ export function isIntersectionOf<T extends readonly unknown[]>(
79
79
  guards.every((guard) => guard(value));
80
80
  }
81
81
 
82
- function objectEntriesChecks<T extends object>(a: T, b: object): b is T {
83
- const aKeys = objectKeys(a);
84
- const bKeySet = new Set(objectKeys(b));
85
- return (
86
- aKeys.length === bKeySet.size &&
87
- aKeys.every((k) => bKeySet.has(k) && isExact(a[k], true)(b[k]))
82
+ export function isExact<const T>(expected: T): Guard<T> {
83
+ // Primitive checks
84
+ if (typeof expected !== "object" || expected === null) {
85
+ // NaN check
86
+ if (typeof expected === "number" && Number.isNaN(expected)) {
87
+ return (value): value is T =>
88
+ typeof value === "number" && Number.isNaN(value);
89
+ }
90
+ return (value): value is T => value === expected;
91
+ }
92
+
93
+ // Date checks
94
+ if (expected instanceof Date) {
95
+ return (value): value is T =>
96
+ value instanceof Date && expected.getTime() === value.getTime();
97
+ }
98
+
99
+ // RegExp checks
100
+ if (expected instanceof RegExp) {
101
+ return (value): value is T =>
102
+ value instanceof RegExp &&
103
+ expected.source === value.source &&
104
+ expected.flags === value.flags;
105
+ }
106
+
107
+ // Error checks
108
+ if (expected instanceof Error) {
109
+ return (value): value is T =>
110
+ value instanceof Error &&
111
+ expected.name === value.name &&
112
+ expected.message === value.message;
113
+ }
114
+
115
+ // Map checks
116
+ if (expected instanceof Map) {
117
+ const guards = Array.from(
118
+ expected
119
+ .entries()
120
+ .map(([k, v]) => [k, isExact(v)] as [string, Guard<unknown>]),
121
+ );
122
+
123
+ return (value): value is T =>
124
+ value instanceof Map &&
125
+ expected.size === value.size &&
126
+ guards.every(([k, guard]) => value.has(k) && guard(value.get(k)));
127
+ }
128
+
129
+ // Set checks
130
+ if (expected instanceof Set) {
131
+ const guards = new Map(
132
+ expected.values().map((v): [unknown, Guard<unknown>] => [v, isExact(v)]),
133
+ );
134
+
135
+ return (value): value is T =>
136
+ value instanceof Set &&
137
+ expected.size === value.size &&
138
+ value.values().every((v) => guards.get(v)?.(v));
139
+ }
140
+
141
+ // Typed Array checks
142
+ if (ArrayBuffer.isView(expected) && !(expected instanceof DataView)) {
143
+ const guards = Array.from(expected as unknown as ArrayLike<number>).map(
144
+ (v) => isExact(v),
145
+ );
146
+
147
+ return (value): value is T =>
148
+ ArrayBuffer.isView(value) &&
149
+ !(value instanceof DataView) &&
150
+ expected.constructor === value.constructor &&
151
+ (expected as unknown as ArrayLike<number>).length ===
152
+ (value as unknown as ArrayLike<number>).length &&
153
+ guards.every((guard, i) =>
154
+ guard((value as unknown as ArrayLike<number>)[i]),
155
+ );
156
+ }
157
+
158
+ // Array checks
159
+ if (Array.isArray(expected)) {
160
+ const guards = expected.map((v) => isExact(v));
161
+
162
+ return (value): value is T =>
163
+ Array.isArray(value) &&
164
+ expected.length === value.length &&
165
+ guards.every((guard, i) => guard(value[i]));
166
+ }
167
+
168
+ // Object checks
169
+ const guards = objectKeys(expected).map(
170
+ (k) => [k, isExact(expected[k])] as [ObjectKey, Guard<unknown>],
88
171
  );
89
- }
90
172
 
91
- export function isExact<const T>(expected: T, deep: boolean = true): Guard<T> {
173
+ function objectEntriesChecks(value: object): value is T & object {
174
+ const valueKeys = new Set(objectKeys(value) as ObjectKey[]);
175
+ return (
176
+ guards.length === valueKeys.size &&
177
+ guards.every(
178
+ ([k, guard]) =>
179
+ valueKeys.has(k) && guard((value as Record<ObjectKey, unknown>)[k]),
180
+ )
181
+ );
182
+ }
183
+
92
184
  return (value): value is T =>
93
- // Shallow checks
94
- expected === value ||
95
- (Number.isNaN(expected) && Number.isNaN(value)) ||
96
- (deep &&
97
- (Array.isArray(expected)
98
- ? // Array checks
99
- Array.isArray(value) &&
100
- expected.length === value.length &&
101
- expected.every((v, i) => isExact(v, true)(value[i]))
102
- : // Object checks
103
- expected != null &&
104
- value != null &&
105
- typeof expected === "object" &&
106
- typeof value === "object" &&
107
- !Array.isArray(value) &&
108
- objectEntriesChecks(expected, value)));
185
+ typeof value === "object" &&
186
+ value != null &&
187
+ !Array.isArray(value) &&
188
+ objectKeys(value).length === guards.length &&
189
+ objectEntriesChecks(value);
109
190
  }
110
191
 
111
192
  // eslint-disable-next-line @typescript-eslint/no-explicit-any