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 CHANGED
@@ -52,15 +52,19 @@ 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
- 4. [isRecordOf](#isrecordof)
61
- 5. [isObjectOf](#isobjectof)
62
- 4. [guardOrThrow](#guardorthrow)
63
- 5. [TypeFromGuard](#typefromguard)
61
+ 4. [isTupleOf](#istupleof)
62
+ 5. [isRecordOf](#isrecordof)
63
+ 6. [isObjectOf](#isobjectof)
64
+ 4. [Macros](#macros)
65
+ 1. [isDiscriminatedObjectOf](#isdiscriminatedobjectof)
66
+ 5. [guardOrThrow](#guardorthrow)
67
+ 6. [TypeFromGuard](#typefromguard)
64
68
 
65
69
  ## Terminology
66
70
 
@@ -123,7 +127,11 @@ It's very useful for enumerations, where you only have a few specific values, e.
123
127
 
124
128
  ### isUnionOf
125
129
 
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.
130
+ 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.
131
+
132
+ ### isIntersectionOf
133
+
134
+ 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
135
 
128
136
  ### isExact
129
137
 
@@ -153,6 +161,21 @@ Higher order guard. This will pass if the incoming value is an array which conta
153
161
 
154
162
  NOTE: This passes for empty arrays
155
163
 
164
+ ### isTupleOf
165
+
166
+ Higher order guard. This takes in any number of guards, and then checks that the incoming value is an array of the same size, with the guards guarding the items in the same order as they appear.
167
+
168
+ For example:
169
+
170
+ ```ts
171
+ const myTupleGuard = isTupleOf(isNumber, isString, isBoolean);
172
+ const value: unknown = [1, "foo", true];
173
+
174
+ if (myTupleGuard(value)) {
175
+ // value passes
176
+ }
177
+ ```
178
+
156
179
  ### isRecordOf
157
180
 
158
181
  Higher order guard. It has two guard parameters, where the first is the key guard, and then the second is a value guard which is optional. If you don't pass in a value guard, the returned guard function has `unknown`s as the value type.
@@ -168,6 +191,56 @@ As seen in the example at the start of this readme, you can do all sorts of comp
168
191
  NOTE: This throws an error if you give it an empty object.\
169
192
  It will also accept an object which contains keys which are not specified.
170
193
 
194
+ ## Macros
195
+
196
+ These are common use cases for guarding setups, where they are made entirely out of the above guard suite.
197
+
198
+ ### isDiscriminatedObjectOf
199
+
200
+ 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>`.
201
+ 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:
202
+
203
+ ```ts
204
+ interface Car {
205
+ wheels: 4;
206
+ owner: string;
207
+ passengers: {
208
+ name: string;
209
+ }[];
210
+ }
211
+
212
+ interface Bike {
213
+ wheels: 2;
214
+ owner: string;
215
+ storage?: string[];
216
+ }
217
+
218
+ type Vehicle = ({ type: "car" } & Car) | ({ type: "bike" } & Bike);
219
+
220
+ // Can then be represented like so in guards:
221
+
222
+ const carGuard = isObjectOf({
223
+ wheels: isExact(4),
224
+ owner: isString,
225
+ passengers: isArrayOf(
226
+ isObjectOf({
227
+ name: isString,
228
+ })
229
+ ),
230
+ });
231
+
232
+ const bikeGuard = isObjectOf({
233
+ wheels: isExact(2),
234
+ owner: isString,
235
+ storage: isOptional(isArrayOf(isString)),
236
+ });
237
+
238
+ const vehicleGuard = isUnionOf(
239
+ isDiscriminatedObjectOf("car", carGuard),
240
+ isDiscriminatedObjectOf("bike", bikeGuard)
241
+ );
242
+ ```
243
+
171
244
  ## guardOrThrow
172
245
 
173
246
  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.
@@ -1,8 +1,14 @@
1
- import { Guard, GuardSchemaOf } from "./types.js";
1
+ import { Guard, GuardSchemaOf } from "./types";
2
2
  export declare function isOptional<T>(guard: Guard<T>): Guard<T | undefined>;
3
3
  export declare function isNullable<T>(guard: Guard<T>): Guard<T | null | undefined>;
4
4
  export declare function isNonNullable<T>(value: T | null | undefined): value is T;
5
5
  export declare function isNot<const N>(guard: Guard<N>): <const T>(value: T | N) => value is T;
6
6
  export declare function isOneOf<const T extends (string | number | boolean | symbol | null | undefined)[]>(...values: T): Guard<(typeof values)[number]>;
7
7
  export declare function isUnionOf<T extends readonly unknown[]>(...guards: GuardSchemaOf<T>): Guard<T[number]>;
8
+ type ArrayToIntersection<A extends readonly unknown[]> = A extends [
9
+ infer T,
10
+ ...infer R
11
+ ] ? T & ArrayToIntersection<R> : unknown;
12
+ export declare function isIntersectionOf<T extends readonly unknown[]>(...guards: GuardSchemaOf<T>): Guard<ArrayToIntersection<T>>;
8
13
  export declare function isExact<const T>(expected: T, deep?: boolean): Guard<T>;
14
+ export {};
package/dist/compound.js CHANGED
@@ -2,7 +2,7 @@ export function isOptional(guard) {
2
2
  if (typeof guard !== "function") {
3
3
  throw new TypeError(`isOptional expects a guard parameter. Got instead: ${guard}`);
4
4
  }
5
- return ((value) => value === undefined || guard(value));
5
+ return (value) => value === undefined || guard(value);
6
6
  }
7
7
  export function isNullable(guard) {
8
8
  if (typeof guard !== "function") {
@@ -29,6 +29,12 @@ export function isUnionOf(...guards) {
29
29
  }
30
30
  return (value) => guards.some((guard) => guard(value));
31
31
  }
32
+ export function isIntersectionOf(...guards) {
33
+ if (guards.every((guard) => typeof guard !== "function")) {
34
+ throw new TypeError(`isIntersectionOf expects N guard parameters. Got instead: ${guards}`);
35
+ }
36
+ return (value) => guards.every((guard) => guard(value));
37
+ }
32
38
  function isEqual(a, b) {
33
39
  return (a === b ||
34
40
  (a != null &&
@@ -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/dist/errors.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Guard } from "./types.js";
1
+ import { Guard } from "./types";
2
2
  export declare class GuardError extends Error {
3
3
  name: string;
4
4
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- export * from "./compound.js";
2
- export * from "./errors.js";
3
- export * from "./primitives.js";
4
- export * from "./structures.js";
5
- export * from "./types.js";
1
+ export * from "./compound";
2
+ export * from "./errors";
3
+ export * from "./macros";
4
+ export * from "./primitives";
5
+ export * from "./structures";
6
+ export * from "./types";
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
- export * from "./compound.js";
2
- export * from "./errors.js";
3
- export * from "./primitives.js";
4
- export * from "./structures.js";
5
- export * from "./types.js";
1
+ export * from "./compound";
2
+ export * from "./errors";
3
+ export * from "./macros";
4
+ export * from "./primitives";
5
+ export * from "./structures";
6
+ export * from "./types";
6
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"src/","sources":["index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,SAAS,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Guard } from "./types";
2
+ export declare const isDiscriminatedObjectOf: <T extends string, O extends object>(type: T, guard: Guard<O>) => Guard<{
3
+ type: T;
4
+ } & O>;
package/dist/macros.js ADDED
@@ -0,0 +1,4 @@
1
+ import { isExact, isIntersectionOf } from "./compound";
2
+ import { isObjectOf } from "./structures";
3
+ export const isDiscriminatedObjectOf = (type, guard) => isIntersectionOf(isObjectOf({ type: isExact(type) }), guard);
4
+ //# sourceMappingURL=macros.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"macros.js","sourceRoot":"src/","sources":["macros.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,IAAO,EACP,KAAe,EACS,EAAE,CAC1B,gBAAgB,CAAC,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,KAAK,CAE1D,CAAC"}
@@ -1,4 +1,4 @@
1
- import { Guard } from "./types.js";
1
+ import { Guard } from "./types";
2
2
  export declare const isUnknown: Guard<unknown>;
3
3
  export declare const isNull: Guard<null>;
4
4
  export declare const isUndefined: Guard<undefined>;
@@ -1,9 +1,10 @@
1
- import { Guard, GuardSchemaOf } from "./types.js";
1
+ import { Guard, GuardSchemaOf } from "./types";
2
2
  type ObjectKey = string | number | symbol;
3
3
  export declare const isAnyArray: Guard<unknown[]>;
4
4
  export declare const isAnyRecord: Guard<Record<ObjectKey, unknown>>;
5
5
  export declare function isArrayOf<T>(guard: Guard<T>): Guard<T[]>;
6
+ export declare function isTupleOf<T extends readonly unknown[]>(...tupleGuards: GuardSchemaOf<T>): Guard<T>;
6
7
  export declare function isRecordOf<K extends ObjectKey>(keyGuard: Guard<K>): Guard<Record<K, unknown>>;
7
8
  export declare function isRecordOf<K extends ObjectKey, V>(keyGuard: Guard<K>, valueGuard: Guard<V>): Guard<Record<K, V>>;
8
- export declare function isObjectOf<O extends object>(schema: GuardSchemaOf<O>): O extends unknown[] ? never : Guard<O>;
9
+ export declare function isObjectOf<O extends object>(schema: GuardSchemaOf<O>): O extends unknown[] ? never : {} extends O ? never : Guard<O>;
9
10
  export {};
@@ -9,6 +9,14 @@ export function isArrayOf(guard) {
9
9
  }
10
10
  return (value) => Array.isArray(value) && value.every(guard);
11
11
  }
12
+ export function isTupleOf(...tupleGuards) {
13
+ if (tupleGuards.some((guard) => typeof guard !== "function")) {
14
+ throw new TypeError(`isTupleOf expects guard parameters. Got instead: ${JSON.stringify(tupleGuards)}`);
15
+ }
16
+ return (value) => Array.isArray(value) &&
17
+ value.length === tupleGuards.length &&
18
+ tupleGuards.every((guard, i) => guard(value[i]));
19
+ }
12
20
  export function isRecordOf(keyGuard, valueGuard) {
13
21
  if (typeof keyGuard !== "function") {
14
22
  throw new TypeError(`isRecordOf keyGuard expects a guard parameter. Got instead: ${keyGuard}`);
@@ -22,16 +30,16 @@ export function isRecordOf(keyGuard, valueGuard) {
22
30
  objectKeys(value).every((key) => keyGuard(key) && (valueGuard?.(value[key]) ?? true));
23
31
  }
24
32
  export function isObjectOf(schema) {
33
+ if (schema == null || typeof schema !== "object") {
34
+ throw new TypeError(`isObjectOf expects a guard schema object. Got instead: ${schema}`);
35
+ }
25
36
  const schemaKeys = objectKeys(schema);
26
- const schemaUnknown = schema;
27
- if (schemaKeys.length === 0) {
28
- throw new Error("isObjectOf received an empty schema");
37
+ if (Array.isArray(schema) ||
38
+ schemaKeys.some((key) => typeof schema[key] !== "function")) {
39
+ throw new TypeError(`isObjectOf expects a guard schema object. Got instead ${JSON.stringify(schema)}`);
29
40
  }
30
- else if (schemaUnknown == null ||
31
- typeof schemaUnknown !== "object" ||
32
- Array.isArray(schemaUnknown) ||
33
- schemaKeys.some((key) => typeof schemaUnknown[key] !== "function")) {
34
- throw new TypeError(`isObjectOf expects a guard schema. Got instead: ${schemaUnknown}`);
41
+ else if (schemaKeys.length === 0) {
42
+ throw new Error("isObjectOf received an empty schema");
35
43
  }
36
44
  return ((value) => value != null &&
37
45
  typeof value === "object" &&
@@ -1 +1 @@
1
- {"version":3,"file":"structures.js","sourceRoot":"src/","sources":["structures.ts"],"names":[],"mappings":"AAIA,SAAS,UAAU,CAAsB,GAAuB;IAC9D,OAAQ,MAAM,CAAC,mBAAmB,CAAC,GAAG,CAAS,CAAC,MAAM,CACpD,MAAM,CAAC,qBAAqB,CAAC,GAAG,CAAQ,CACzC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,MAAM,UAAU,GAAqB,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAE5E,MAAM,CAAC,MAAM,WAAW,GAAsC,CAC5D,KAAK,EACgC,EAAE,CACvC,KAAK,IAAI,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAEtE,MAAM,UAAU,SAAS,CAAI,KAAe;IAC1C,IAAI,OAAO,KAAK,KAAK,UAAU,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CACjB,qDAAqD,KAAK,EAAE,CAC7D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAgB,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAC7E,CAAC;AASD,MAAM,UAAU,UAAU,CACxB,QAAkB,EAClB,UAAqB;IAErB,IAAI,OAAO,QAAQ,KAAK,UAAU,EAAE,CAAC;QACnC,MAAM,IAAI,SAAS,CACjB,+DAA+D,QAAQ,EAAE,CAC1E,CAAC;IACJ,CAAC;SAAM,IAAI,UAAU,IAAI,IAAI,IAAI,OAAO,UAAU,KAAK,UAAU,EAAE,CAAC;QAClE,MAAM,IAAI,SAAS,CACjB,2EAA2E,UAAU,EAAE,CACxF,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,KAAK,EAAyB,EAAE,CACtC,KAAK,IAAI,IAAI;QACb,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,UAAU,CAAC,KAAK,CAAC,CAAC,KAAK,CACrB,CAAC,GAAG,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAC7D,CAAC;AACN,CAAC;AAED,MAAM,UAAU,UAAU,CACxB,MAAwB;IAExB,MAAM,UAAU,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,aAAa,GAAG,MAAiB,CAAC;IACxC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;SAAM,IACL,aAAa,IAAI,IAAI;QACrB,OAAO,aAAa,KAAK,QAAQ;QACjC,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC;QAC5B,UAAU,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,OAAQ,aAAmB,CAAC,GAAG,CAAC,KAAK,UAAU,CAAC,EACzE,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,mDAAmD,aAAa,EAAE,CACnE,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,CAAC,KAAK,EAAc,EAAE,CAC5B,KAAK,IAAI,IAAI;QACb,OAAO,KAAK,KAAK,QAAQ;QACzB,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACrB,UAAU,CAAC,KAAK,CACd,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,MAAM,CAAC,GAAG,CAAC,CAAE,KAAW,CAAC,GAAG,CAAC,CAAC,CACxD,CAA2C,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"structures.js","sourceRoot":"src/","sources":["structures.ts"],"names":[],"mappings":"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"}
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.3",
12
12
  "scripts": {
13
13
  "test": "jest",
14
14
  "build": "tsc",
@@ -28,15 +28,15 @@
28
28
  "@types/eslint": "^9.6.1",
29
29
  "@types/eslint__js": "^8.42.3",
30
30
  "@types/jest": "^29.5.14",
31
- "@typescript-eslint/eslint-plugin": "^8.15.0",
32
- "@typescript-eslint/parser": "^8.15.0",
31
+ "@typescript-eslint/eslint-plugin": "^8.16.0",
32
+ "@typescript-eslint/parser": "^8.16.0",
33
33
  "babel-jest": "^29.7.0",
34
34
  "eslint": "^9.15.0",
35
35
  "eslint-plugin-eslint-comments": "^3.2.0",
36
36
  "eslint-plugin-import": "^2.31.0",
37
37
  "eslint-plugin-jest": "^28.9.0",
38
38
  "jest": "^29.7.0",
39
- "typescript": "^5.6.3",
40
- "typescript-eslint": "^8.15.0"
39
+ "typescript": "^5.7.2",
40
+ "typescript-eslint": "^8.16.0"
41
41
  }
42
42
  }
package/src/compound.ts CHANGED
@@ -1,81 +1,99 @@
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";
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 is T | undefined => value === undefined || guard(value);
11
+ }
12
+
13
+ export function isNullable<T>(guard: Guard<T>): Guard<T | null | undefined> {
14
+ if (typeof guard !== "function") {
15
+ throw new TypeError(
16
+ `isNullable expects a guard parameter. Got instead: ${guard}`
17
+ );
18
+ }
19
+
20
+ return (value: unknown): value is T | null | undefined =>
21
+ value == null || guard(value);
22
+ }
23
+
24
+ export function isNonNullable<T>(value: T | null | undefined): value is T {
25
+ return value != null;
26
+ }
27
+
28
+ export function isNot<const N>(guard: Guard<N>) {
29
+ if (typeof guard !== "function") {
30
+ throw new TypeError(
31
+ `isNot expects a guard parameter. Got instead: ${guard}`
32
+ );
33
+ }
34
+
35
+ return <const T>(value: T | N): value is T => !guard(value);
36
+ }
37
+
38
+ export function isOneOf<
39
+ const T extends (string | number | boolean | symbol | null | undefined)[]
40
+ >(...values: T): Guard<(typeof values)[number]> {
41
+ const valueSet = new Set(values);
42
+ return (value: unknown): value is T[number] =>
43
+ valueSet.has(value as T[number]);
44
+ }
45
+
46
+ export function isUnionOf<T extends readonly unknown[]>(
47
+ ...guards: GuardSchemaOf<T>
48
+ ): Guard<T[number]> {
49
+ if (guards.every((guard) => typeof guard !== "function")) {
50
+ throw new TypeError(
51
+ `isUnionOf expects N guard parameters. Got instead: ${guards}`
52
+ );
53
+ }
54
+
55
+ return (value): value is T => guards.some((guard) => guard(value));
56
+ }
57
+
58
+ type ArrayToIntersection<A extends readonly unknown[]> = A extends [
59
+ infer T,
60
+ ...infer R
61
+ ]
62
+ ? T & ArrayToIntersection<R>
63
+ : unknown;
64
+
65
+ export function isIntersectionOf<T extends readonly unknown[]>(
66
+ ...guards: GuardSchemaOf<T>
67
+ ): Guard<ArrayToIntersection<T>> {
68
+ if (guards.every((guard) => typeof guard !== "function")) {
69
+ throw new TypeError(
70
+ `isIntersectionOf expects N guard parameters. Got instead: ${guards}`
71
+ );
72
+ }
73
+
74
+ return (value): value is ArrayToIntersection<T> =>
75
+ guards.every((guard) => guard(value));
76
+ }
77
+
78
+ function isEqual<T>(a: T, b: unknown): b is T {
79
+ return (
80
+ a === b ||
81
+ (a != null &&
82
+ b != null &&
83
+ typeof a === "object" &&
84
+ typeof b === "object" &&
85
+ (Array.isArray(a)
86
+ ? Array.isArray(b) &&
87
+ a.length === b.length &&
88
+ a.every((v, i) => isEqual(v, b[i]))
89
+ : Object.keys(a).length === Object.keys(b).length &&
90
+ Object.entries(a).every(
91
+ ([k, v]) => k in b && isEqual(v, (b as Record<string, unknown>)[k])
92
+ )))
93
+ );
94
+ }
95
+
96
+ export function isExact<const T>(expected: T, deep: boolean = true): Guard<T> {
97
+ return (value): value is T =>
98
+ deep ? isEqual(expected, value) : expected === value;
99
+ }
package/src/errors.ts CHANGED
@@ -1,17 +1,17 @@
1
- import { Guard } from "./types.js";
2
-
3
- export class GuardError extends Error {
4
- name = "GuardError";
5
- }
6
-
7
- export function guardOrThrow<T>(
8
- value: unknown,
9
- guard: Guard<T>,
10
- hint?: string
11
- ): T {
12
- if (guard(value)) {
13
- return value;
14
- } else {
15
- throw new GuardError(hint ?? "Guard error");
16
- }
17
- }
1
+ import { Guard } from "./types";
2
+
3
+ export class GuardError extends Error {
4
+ name = "GuardError";
5
+ }
6
+
7
+ export function guardOrThrow<T>(
8
+ value: unknown,
9
+ guard: Guard<T>,
10
+ hint?: string
11
+ ): T {
12
+ if (guard(value)) {
13
+ return value;
14
+ } else {
15
+ throw new GuardError(hint ?? "Guard error");
16
+ }
17
+ }
package/src/index.ts CHANGED
@@ -1,5 +1,6 @@
1
- export * from "./compound.js";
2
- export * from "./errors.js";
3
- export * from "./primitives.js";
4
- export * from "./structures.js";
5
- export * from "./types.js";
1
+ export * from "./compound";
2
+ export * from "./errors";
3
+ export * from "./macros";
4
+ export * from "./primitives";
5
+ export * from "./structures";
6
+ export * from "./types";
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) satisfies Guard<
10
+ { type: T } & O
11
+ >;
package/src/primitives.ts CHANGED
@@ -1,23 +1,23 @@
1
- import { Guard } from "./types.js";
2
-
3
- export const isUnknown: Guard<unknown> = (_value): _value is unknown => true;
4
-
5
- export const isNull: Guard<null> = (value) => value === null;
6
-
7
- export const isUndefined: Guard<undefined> = (value) => value === undefined;
8
-
9
- export const isNumber: Guard<number> = (value) => typeof value === "number";
10
-
11
- export const isInteger: Guard<number> = (value): value is number =>
12
- Number.isInteger(value);
13
-
14
- export const isString: Guard<string> = (value) => typeof value === "string";
15
-
16
- export const isSymbol: Guard<symbol> = (value) => typeof value === "symbol";
17
-
18
- export const isBoolean: Guard<boolean> = (value) =>
19
- value === true || value === false;
20
-
21
- export const isFunction: Guard<(...args: unknown[]) => unknown> = (
22
- value
23
- ): value is (...args: unknown[]) => unknown => typeof value === "function";
1
+ import { Guard } from "./types";
2
+
3
+ export const isUnknown: Guard<unknown> = (_value): _value is unknown => true;
4
+
5
+ export const isNull: Guard<null> = (value) => value === null;
6
+
7
+ export const isUndefined: Guard<undefined> = (value) => value === undefined;
8
+
9
+ export const isNumber: Guard<number> = (value) => typeof value === "number";
10
+
11
+ export const isInteger: Guard<number> = (value): value is number =>
12
+ Number.isInteger(value);
13
+
14
+ export const isString: Guard<string> = (value) => typeof value === "string";
15
+
16
+ export const isSymbol: Guard<symbol> = (value) => typeof value === "symbol";
17
+
18
+ export const isBoolean: Guard<boolean> = (value) =>
19
+ value === true || value === false;
20
+
21
+ export const isFunction: Guard<(...args: unknown[]) => unknown> = (
22
+ value
23
+ ): value is (...args: unknown[]) => unknown => typeof value === "function";