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 +12 -7
- package/dist/compound.d.ts +1 -1
- package/dist/compound.js +70 -24
- package/dist/compound.js.map +1 -1
- package/eslint.config.js +3 -1
- package/package.json +1 -1
- package/src/compound.test.ts +94 -14
- package/src/compound.ts +106 -25
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
|
|
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,
|
|
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
|
|
package/dist/compound.d.ts
CHANGED
|
@@ -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
|
|
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
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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) {
|
package/dist/compound.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compound.js","sourceRoot":"src/","sources":["compound.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAK1C,MAAM,UAAU,UAAU,CAAI,KAAe;IAC3C,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CACjB,sDAAsD,KAAK,EAAE,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAA0B,EAAE,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,UAAU,CAAI,KAAe;IAC3C,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CACjB,sDAAsD,KAAK,EAAE,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAc,EAAiC,EAAE,CACvD,KAAK,IAAI,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,KAA2B;IAE3B,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,KAAK,CAAU,KAAe;IAC5C,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CACjB,iDAAiD,KAAK,EAAE,CACzD,CAAC;IACJ,CAAC;IAED,OAAO,CAAU,KAAY,EAAc,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,OAAO,CAErB,GAAG,MAAS;IACZ,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAE,QAAQ,CAAC,GAAwB,CAAC,KAAK,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,SAAS,CACvB,GAAG,MAAwB;IAE3B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CACjB,sDAAsD,MAAM,EAAE,CAC/D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAc,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AACrE,CAAC;AASD,MAAM,UAAU,gBAAgB,CAC9B,GAAG,MAAwB;IAE3B,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,UAAU,CAAC,EAAE,CAAC;QACzD,MAAM,IAAI,SAAS,CACjB,6DAA6D,MAAM,EAAE,CACtE,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAmC,EAAE,CAChD,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;AAC1C,CAAC;AAED,
|
|
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
package/src/compound.test.ts
CHANGED
|
@@ -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"
|
|
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
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
(
|
|
97
|
-
|
|
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
|