effect 3.16.13 → 3.16.14

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.
Files changed (67) hide show
  1. package/dist/cjs/Predicate.js +516 -200
  2. package/dist/cjs/Predicate.js.map +1 -1
  3. package/dist/cjs/Schema.js +2 -2
  4. package/dist/cjs/Schema.js.map +1 -1
  5. package/dist/cjs/internal/channel.js +2 -2
  6. package/dist/cjs/internal/channel.js.map +1 -1
  7. package/dist/cjs/internal/core-effect.js +1 -0
  8. package/dist/cjs/internal/core-effect.js.map +1 -1
  9. package/dist/cjs/internal/dataSource.js +2 -2
  10. package/dist/cjs/internal/dataSource.js.map +1 -1
  11. package/dist/cjs/internal/fiberRuntime.js +1 -1
  12. package/dist/cjs/internal/fiberRuntime.js.map +1 -1
  13. package/dist/cjs/internal/groupBy.js +2 -2
  14. package/dist/cjs/internal/groupBy.js.map +1 -1
  15. package/dist/cjs/internal/sink.js +4 -4
  16. package/dist/cjs/internal/sink.js.map +1 -1
  17. package/dist/cjs/internal/stm/stm.js +3 -3
  18. package/dist/cjs/internal/stm/stm.js.map +1 -1
  19. package/dist/cjs/internal/stream.js +5 -5
  20. package/dist/cjs/internal/stream.js.map +1 -1
  21. package/dist/cjs/internal/version.js +1 -1
  22. package/dist/dts/Effect.d.ts +1 -1
  23. package/dist/dts/Effect.d.ts.map +1 -1
  24. package/dist/dts/Predicate.d.ts +1190 -375
  25. package/dist/dts/Predicate.d.ts.map +1 -1
  26. package/dist/dts/index.d.ts +15 -0
  27. package/dist/dts/index.d.ts.map +1 -1
  28. package/dist/dts/internal/core-effect.d.ts.map +1 -1
  29. package/dist/dts/internal/stm/stm.d.ts.map +1 -1
  30. package/dist/dts/internal/stream.d.ts.map +1 -1
  31. package/dist/esm/Predicate.js +516 -200
  32. package/dist/esm/Predicate.js.map +1 -1
  33. package/dist/esm/Schema.js +2 -2
  34. package/dist/esm/Schema.js.map +1 -1
  35. package/dist/esm/index.js +15 -0
  36. package/dist/esm/index.js.map +1 -1
  37. package/dist/esm/internal/channel.js +2 -2
  38. package/dist/esm/internal/channel.js.map +1 -1
  39. package/dist/esm/internal/core-effect.js +1 -0
  40. package/dist/esm/internal/core-effect.js.map +1 -1
  41. package/dist/esm/internal/dataSource.js +2 -2
  42. package/dist/esm/internal/dataSource.js.map +1 -1
  43. package/dist/esm/internal/fiberRuntime.js +1 -1
  44. package/dist/esm/internal/fiberRuntime.js.map +1 -1
  45. package/dist/esm/internal/groupBy.js +2 -2
  46. package/dist/esm/internal/groupBy.js.map +1 -1
  47. package/dist/esm/internal/sink.js +4 -4
  48. package/dist/esm/internal/sink.js.map +1 -1
  49. package/dist/esm/internal/stm/stm.js +3 -3
  50. package/dist/esm/internal/stm/stm.js.map +1 -1
  51. package/dist/esm/internal/stream.js +5 -5
  52. package/dist/esm/internal/stream.js.map +1 -1
  53. package/dist/esm/internal/version.js +1 -1
  54. package/package.json +1 -1
  55. package/src/Effect.ts +1 -1
  56. package/src/Predicate.ts +1213 -377
  57. package/src/Schema.ts +2 -2
  58. package/src/index.ts +15 -0
  59. package/src/internal/channel.ts +2 -2
  60. package/src/internal/core-effect.ts +1 -0
  61. package/src/internal/dataSource.ts +12 -14
  62. package/src/internal/fiberRuntime.ts +2 -2
  63. package/src/internal/groupBy.ts +12 -14
  64. package/src/internal/sink.ts +13 -15
  65. package/src/internal/stm/stm.ts +16 -20
  66. package/src/internal/stream.ts +17 -23
  67. package/src/internal/version.ts +1 -1
package/src/Predicate.ts CHANGED
@@ -1,4 +1,19 @@
1
1
  /**
2
+ * This module provides a collection of functions for working with predicates and refinements.
3
+ *
4
+ * A `Predicate<A>` is a function that takes a value of type `A` and returns a boolean.
5
+ * It is used to check if a value satisfies a certain condition.
6
+ *
7
+ * A `Refinement<A, B>` is a special type of predicate that not only checks a condition
8
+ * but also provides a type guard, allowing TypeScript to narrow the type of the input
9
+ * value from `A` to a more specific type `B` within a conditional block.
10
+ *
11
+ * The module includes:
12
+ * - Basic predicates and refinements for common types (e.g., `isString`, `isNumber`).
13
+ * - Combinators to create new predicates from existing ones (e.g., `and`, `or`, `not`).
14
+ * - Advanced combinators for working with data structures (e.g., `tuple`, `struct`).
15
+ * - Type-level utilities for inspecting predicate and refinement types.
16
+ *
2
17
  * @since 2.0.0
3
18
  */
4
19
  import { dual, isFunction as isFunction_ } from "./Function.js"
@@ -6,6 +21,20 @@ import type { TypeLambda } from "./HKT.js"
6
21
  import type { TupleOf, TupleOfAtLeast } from "./Types.js"
7
22
 
8
23
  /**
24
+ * Represents a function that takes a value of type `A` and returns `true` if the value
25
+ * satisfies some condition, `false` otherwise.
26
+ *
27
+ * @example
28
+ * ```ts
29
+ * import { Predicate } from "effect"
30
+ * import * as assert from "node:assert"
31
+ *
32
+ * const isEven: Predicate.Predicate<number> = (n) => n % 2 === 0
33
+ *
34
+ * assert.strictEqual(isEven(2), true)
35
+ * assert.strictEqual(isEven(3), false)
36
+ * ```
37
+ *
9
38
  * @category models
10
39
  * @since 2.0.0
11
40
  */
@@ -14,6 +43,9 @@ export interface Predicate<in A> {
14
43
  }
15
44
 
16
45
  /**
46
+ * A `TypeLambda` for `Predicate`. This is used to support higher-kinded types
47
+ * and allows `Predicate` to be used in generic contexts within the `effect` ecosystem.
48
+ *
17
49
  * @category type lambdas
18
50
  * @since 2.0.0
19
51
  */
@@ -22,6 +54,27 @@ export interface PredicateTypeLambda extends TypeLambda {
22
54
  }
23
55
 
24
56
  /**
57
+ * Represents a function that serves as a type guard.
58
+ *
59
+ * A `Refinement<A, B>` is a function that takes a value of type `A` and returns a
60
+ * type predicate `a is B`, where `B` is a subtype of `A`. If the function returns
61
+ * `true`, TypeScript will narrow the type of the input variable to `B`.
62
+ *
63
+ * @example
64
+ * ```ts
65
+ * import { Predicate } from "effect"
66
+ * import * as assert from "node:assert"
67
+ *
68
+ * const isString: Predicate.Refinement<unknown, string> = (u): u is string => typeof u === "string"
69
+ *
70
+ * const value: unknown = "hello"
71
+ *
72
+ * if (isString(value)) {
73
+ * // value is now known to be a string
74
+ * assert.strictEqual(value.toUpperCase(), "HELLO")
75
+ * }
76
+ * ```
77
+ *
25
78
  * @category models
26
79
  * @since 2.0.0
27
80
  */
@@ -30,16 +83,29 @@ export interface Refinement<in A, out B extends A> {
30
83
  }
31
84
 
32
85
  /**
86
+ * A namespace for type-level utilities for `Predicate`.
87
+ *
33
88
  * @since 3.6.0
34
89
  * @category type-level
35
90
  */
36
91
  export declare namespace Predicate {
37
92
  /**
93
+ * Extracts the input type `A` from a `Predicate<A>`.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * import { type Predicate } from "effect"
98
+ *
99
+ * type T = Predicate.Predicate.In<Predicate.Predicate<string>> // T is string
100
+ * ```
101
+ *
38
102
  * @since 3.6.0
39
103
  * @category type-level
40
104
  */
41
105
  export type In<T extends Any> = [T] extends [Predicate<infer _A>] ? _A : never
42
106
  /**
107
+ * A type representing any `Predicate`.
108
+ *
43
109
  * @since 3.6.0
44
110
  * @category type-level
45
111
  */
@@ -47,21 +113,45 @@ export declare namespace Predicate {
47
113
  }
48
114
 
49
115
  /**
116
+ * A namespace for type-level utilities for `Refinement`.
117
+ *
50
118
  * @since 3.6.0
51
119
  * @category type-level
52
120
  */
53
121
  export declare namespace Refinement {
54
122
  /**
123
+ * Extracts the input type `A` from a `Refinement<A, B>`.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * import { type Predicate } from "effect"
128
+ *
129
+ * type IsString = Predicate.Refinement<unknown, string>
130
+ * type T = Predicate.Refinement.In<IsString> // T is unknown
131
+ * ```
132
+ *
55
133
  * @since 3.6.0
56
134
  * @category type-level
57
135
  */
58
136
  export type In<T extends Any> = [T] extends [Refinement<infer _A, infer _>] ? _A : never
59
137
  /**
138
+ * Extracts the output (refined) type `B` from a `Refinement<A, B>`.
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * import { type Predicate } from "effect"
143
+ *
144
+ * type IsString = Predicate.Refinement<unknown, string>
145
+ * type T = Predicate.Refinement.Out<IsString> // T is string
146
+ * ```
147
+ *
60
148
  * @since 3.6.0
61
149
  * @category type-level
62
150
  */
63
151
  export type Out<T extends Any> = [T] extends [Refinement<infer _, infer _B>] ? _B : never
64
152
  /**
153
+ * A type representing any `Refinement`.
154
+ *
65
155
  * @since 3.6.0
66
156
  * @category type-level
67
157
  */
@@ -69,19 +159,26 @@ export declare namespace Refinement {
69
159
  }
70
160
 
71
161
  /**
72
- * Given a `Predicate<A>` returns a `Predicate<B>`
162
+ * Transforms a `Predicate<A>` into a `Predicate<B>` by applying a function `(b: B) => A`
163
+ * to the input before passing it to the predicate. This is also known as "contramap" or
164
+ * "pre-composition".
73
165
  *
74
166
  * @example
75
167
  * ```ts
76
- * import * as assert from "node:assert"
77
168
  * import { Predicate, Number } from "effect"
169
+ * import * as assert from "node:assert"
78
170
  *
79
- * const minLength3 = Predicate.mapInput(Number.greaterThan(2), (s: string) => s.length)
171
+ * // A predicate on numbers
172
+ * const isPositive: Predicate.Predicate<number> = Number.greaterThan(0)
80
173
  *
81
- * assert.deepStrictEqual(minLength3("a"), false)
82
- * assert.deepStrictEqual(minLength3("aa"), false)
83
- * assert.deepStrictEqual(minLength3("aaa"), true)
84
- * assert.deepStrictEqual(minLength3("aaaa"), true)
174
+ * // A function from `string` to `number`
175
+ * const stringLength = (s: string): number => s.length
176
+ *
177
+ * // Create a new predicate on strings by mapping the input
178
+ * const hasPositiveLength = Predicate.mapInput(isPositive, stringLength)
179
+ *
180
+ * assert.strictEqual(hasPositiveLength("hello"), true)
181
+ * assert.strictEqual(hasPositiveLength(""), false)
85
182
  * ```
86
183
  *
87
184
  * @category combinators
@@ -89,19 +186,26 @@ export declare namespace Refinement {
89
186
  */
90
187
  export const mapInput: {
91
188
  /**
92
- * Given a `Predicate<A>` returns a `Predicate<B>`
189
+ * Transforms a `Predicate<A>` into a `Predicate<B>` by applying a function `(b: B) => A`
190
+ * to the input before passing it to the predicate. This is also known as "contramap" or
191
+ * "pre-composition".
93
192
  *
94
193
  * @example
95
194
  * ```ts
96
- * import * as assert from "node:assert"
97
195
  * import { Predicate, Number } from "effect"
196
+ * import * as assert from "node:assert"
98
197
  *
99
- * const minLength3 = Predicate.mapInput(Number.greaterThan(2), (s: string) => s.length)
198
+ * // A predicate on numbers
199
+ * const isPositive: Predicate.Predicate<number> = Number.greaterThan(0)
100
200
  *
101
- * assert.deepStrictEqual(minLength3("a"), false)
102
- * assert.deepStrictEqual(minLength3("aa"), false)
103
- * assert.deepStrictEqual(minLength3("aaa"), true)
104
- * assert.deepStrictEqual(minLength3("aaaa"), true)
201
+ * // A function from `string` to `number`
202
+ * const stringLength = (s: string): number => s.length
203
+ *
204
+ * // Create a new predicate on strings by mapping the input
205
+ * const hasPositiveLength = Predicate.mapInput(isPositive, stringLength)
206
+ *
207
+ * assert.strictEqual(hasPositiveLength("hello"), true)
208
+ * assert.strictEqual(hasPositiveLength(""), false)
105
209
  * ```
106
210
  *
107
211
  * @category combinators
@@ -109,19 +213,26 @@ export const mapInput: {
109
213
  */
110
214
  <B, A>(f: (b: B) => A): (self: Predicate<A>) => Predicate<B>
111
215
  /**
112
- * Given a `Predicate<A>` returns a `Predicate<B>`
216
+ * Transforms a `Predicate<A>` into a `Predicate<B>` by applying a function `(b: B) => A`
217
+ * to the input before passing it to the predicate. This is also known as "contramap" or
218
+ * "pre-composition".
113
219
  *
114
220
  * @example
115
221
  * ```ts
116
- * import * as assert from "node:assert"
117
222
  * import { Predicate, Number } from "effect"
223
+ * import * as assert from "node:assert"
224
+ *
225
+ * // A predicate on numbers
226
+ * const isPositive: Predicate.Predicate<number> = Number.greaterThan(0)
227
+ *
228
+ * // A function from `string` to `number`
229
+ * const stringLength = (s: string): number => s.length
118
230
  *
119
- * const minLength3 = Predicate.mapInput(Number.greaterThan(2), (s: string) => s.length)
231
+ * // Create a new predicate on strings by mapping the input
232
+ * const hasPositiveLength = Predicate.mapInput(isPositive, stringLength)
120
233
  *
121
- * assert.deepStrictEqual(minLength3("a"), false)
122
- * assert.deepStrictEqual(minLength3("aa"), false)
123
- * assert.deepStrictEqual(minLength3("aaa"), true)
124
- * assert.deepStrictEqual(minLength3("aaaa"), true)
234
+ * assert.strictEqual(hasPositiveLength("hello"), true)
235
+ * assert.strictEqual(hasPositiveLength(""), false)
125
236
  * ```
126
237
  *
127
238
  * @category combinators
@@ -131,23 +242,24 @@ export const mapInput: {
131
242
  } = dual(2, <A, B>(self: Predicate<A>, f: (b: B) => A): Predicate<B> => (b) => self(f(b)))
132
243
 
133
244
  /**
134
- * Determine if an `Array` is a tuple with exactly `N` elements, narrowing down the type to `TupleOf`.
135
- *
136
- * An `Array` is considered to be a `TupleOf` if its length is exactly `N`.
245
+ * A refinement that checks if a `ReadonlyArray<T>` is a tuple with exactly `N` elements.
246
+ * If the check is successful, the type is narrowed to `TupleOf<N, T>`.
137
247
  *
138
248
  * @example
139
249
  * ```ts
140
250
  * import * as assert from "node:assert"
141
251
  * import { isTupleOf } from "effect/Predicate"
142
252
  *
143
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 3), true);
144
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 2), false);
145
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 4), false);
253
+ * const isTupleOf3 = isTupleOf(3)
254
+ *
255
+ * assert.strictEqual(isTupleOf3([1, 2, 3]), true);
256
+ * assert.strictEqual(isTupleOf3([1, 2]), false);
146
257
  *
147
258
  * const arr: number[] = [1, 2, 3];
148
259
  * if (isTupleOf(arr, 3)) {
149
- * console.log(arr);
150
- * // ^? [number, number, number]
260
+ * // The type of arr is now [number, number, number]
261
+ * const [a, b, c] = arr;
262
+ * assert.deepStrictEqual([a, b, c], [1, 2, 3])
151
263
  * }
152
264
  * ```
153
265
  *
@@ -156,23 +268,24 @@ export const mapInput: {
156
268
  */
157
269
  export const isTupleOf: {
158
270
  /**
159
- * Determine if an `Array` is a tuple with exactly `N` elements, narrowing down the type to `TupleOf`.
160
- *
161
- * An `Array` is considered to be a `TupleOf` if its length is exactly `N`.
271
+ * A refinement that checks if a `ReadonlyArray<T>` is a tuple with exactly `N` elements.
272
+ * If the check is successful, the type is narrowed to `TupleOf<N, T>`.
162
273
  *
163
274
  * @example
164
275
  * ```ts
165
276
  * import * as assert from "node:assert"
166
277
  * import { isTupleOf } from "effect/Predicate"
167
278
  *
168
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 3), true);
169
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 2), false);
170
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 4), false);
279
+ * const isTupleOf3 = isTupleOf(3)
280
+ *
281
+ * assert.strictEqual(isTupleOf3([1, 2, 3]), true);
282
+ * assert.strictEqual(isTupleOf3([1, 2]), false);
171
283
  *
172
284
  * const arr: number[] = [1, 2, 3];
173
285
  * if (isTupleOf(arr, 3)) {
174
- * console.log(arr);
175
- * // ^? [number, number, number]
286
+ * // The type of arr is now [number, number, number]
287
+ * const [a, b, c] = arr;
288
+ * assert.deepStrictEqual([a, b, c], [1, 2, 3])
176
289
  * }
177
290
  * ```
178
291
  *
@@ -181,23 +294,24 @@ export const isTupleOf: {
181
294
  */
182
295
  <N extends number>(n: N): <T>(self: ReadonlyArray<T>) => self is TupleOf<N, T>
183
296
  /**
184
- * Determine if an `Array` is a tuple with exactly `N` elements, narrowing down the type to `TupleOf`.
185
- *
186
- * An `Array` is considered to be a `TupleOf` if its length is exactly `N`.
297
+ * A refinement that checks if a `ReadonlyArray<T>` is a tuple with exactly `N` elements.
298
+ * If the check is successful, the type is narrowed to `TupleOf<N, T>`.
187
299
  *
188
300
  * @example
189
301
  * ```ts
190
302
  * import * as assert from "node:assert"
191
303
  * import { isTupleOf } from "effect/Predicate"
192
304
  *
193
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 3), true);
194
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 2), false);
195
- * assert.deepStrictEqual(isTupleOf([1, 2, 3], 4), false);
305
+ * const isTupleOf3 = isTupleOf(3)
306
+ *
307
+ * assert.strictEqual(isTupleOf3([1, 2, 3]), true);
308
+ * assert.strictEqual(isTupleOf3([1, 2]), false);
196
309
  *
197
310
  * const arr: number[] = [1, 2, 3];
198
311
  * if (isTupleOf(arr, 3)) {
199
- * console.log(arr);
200
- * // ^? [number, number, number]
312
+ * // The type of arr is now [number, number, number]
313
+ * const [a, b, c] = arr;
314
+ * assert.deepStrictEqual([a, b, c], [1, 2, 3])
201
315
  * }
202
316
  * ```
203
317
  *
@@ -208,23 +322,25 @@ export const isTupleOf: {
208
322
  } = dual(2, <T, N extends number>(self: ReadonlyArray<T>, n: N): self is TupleOf<N, T> => self.length === n)
209
323
 
210
324
  /**
211
- * Determine if an `Array` is a tuple with at least `N` elements, narrowing down the type to `TupleOfAtLeast`.
212
- *
213
- * An `Array` is considered to be a `TupleOfAtLeast` if its length is at least `N`.
325
+ * A refinement that checks if a `ReadonlyArray<T>` is a tuple with at least `N` elements.
326
+ * If the check is successful, the type is narrowed to `TupleOfAtLeast<N, T>`.
214
327
  *
215
328
  * @example
216
329
  * ```ts
217
330
  * import * as assert from "node:assert"
218
331
  * import { isTupleOfAtLeast } from "effect/Predicate"
219
332
  *
220
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 3), true);
221
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 2), true);
222
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 4), false);
333
+ * const isTupleOfAtLeast3 = isTupleOfAtLeast(3)
334
+ *
335
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3]), true);
336
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3, 4]), true);
337
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2]), false);
223
338
  *
224
339
  * const arr: number[] = [1, 2, 3, 4];
225
340
  * if (isTupleOfAtLeast(arr, 3)) {
226
- * console.log(arr);
227
- * // ^? [number, number, number, ...number[]]
341
+ * // The type of arr is now [number, number, number, ...number[]]
342
+ * const [a, b, c] = arr;
343
+ * assert.deepStrictEqual([a, b, c], [1, 2, 3])
228
344
  * }
229
345
  * ```
230
346
  *
@@ -233,23 +349,25 @@ export const isTupleOf: {
233
349
  */
234
350
  export const isTupleOfAtLeast: {
235
351
  /**
236
- * Determine if an `Array` is a tuple with at least `N` elements, narrowing down the type to `TupleOfAtLeast`.
237
- *
238
- * An `Array` is considered to be a `TupleOfAtLeast` if its length is at least `N`.
352
+ * A refinement that checks if a `ReadonlyArray<T>` is a tuple with at least `N` elements.
353
+ * If the check is successful, the type is narrowed to `TupleOfAtLeast<N, T>`.
239
354
  *
240
355
  * @example
241
356
  * ```ts
242
357
  * import * as assert from "node:assert"
243
358
  * import { isTupleOfAtLeast } from "effect/Predicate"
244
359
  *
245
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 3), true);
246
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 2), true);
247
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 4), false);
360
+ * const isTupleOfAtLeast3 = isTupleOfAtLeast(3)
361
+ *
362
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3]), true);
363
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3, 4]), true);
364
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2]), false);
248
365
  *
249
366
  * const arr: number[] = [1, 2, 3, 4];
250
367
  * if (isTupleOfAtLeast(arr, 3)) {
251
- * console.log(arr);
252
- * // ^? [number, number, number, ...number[]]
368
+ * // The type of arr is now [number, number, number, ...number[]]
369
+ * const [a, b, c] = arr;
370
+ * assert.deepStrictEqual([a, b, c], [1, 2, 3])
253
371
  * }
254
372
  * ```
255
373
  *
@@ -258,23 +376,25 @@ export const isTupleOfAtLeast: {
258
376
  */
259
377
  <N extends number>(n: N): <T>(self: ReadonlyArray<T>) => self is TupleOfAtLeast<N, T>
260
378
  /**
261
- * Determine if an `Array` is a tuple with at least `N` elements, narrowing down the type to `TupleOfAtLeast`.
262
- *
263
- * An `Array` is considered to be a `TupleOfAtLeast` if its length is at least `N`.
379
+ * A refinement that checks if a `ReadonlyArray<T>` is a tuple with at least `N` elements.
380
+ * If the check is successful, the type is narrowed to `TupleOfAtLeast<N, T>`.
264
381
  *
265
382
  * @example
266
383
  * ```ts
267
384
  * import * as assert from "node:assert"
268
385
  * import { isTupleOfAtLeast } from "effect/Predicate"
269
386
  *
270
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 3), true);
271
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 2), true);
272
- * assert.deepStrictEqual(isTupleOfAtLeast([1, 2, 3], 4), false);
387
+ * const isTupleOfAtLeast3 = isTupleOfAtLeast(3)
388
+ *
389
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3]), true);
390
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2, 3, 4]), true);
391
+ * assert.strictEqual(isTupleOfAtLeast3([1, 2]), false);
273
392
  *
274
393
  * const arr: number[] = [1, 2, 3, 4];
275
394
  * if (isTupleOfAtLeast(arr, 3)) {
276
- * console.log(arr);
277
- * // ^? [number, number, number, ...number[]]
395
+ * // The type of arr is now [number, number, number, ...number[]]
396
+ * const [a, b, c] = arr;
397
+ * assert.deepStrictEqual([a, b, c], [1, 2, 3])
278
398
  * }
279
399
  * ```
280
400
  *
@@ -285,16 +405,22 @@ export const isTupleOfAtLeast: {
285
405
  } = dual(2, <T, N extends number>(self: ReadonlyArray<T>, n: N): self is TupleOfAtLeast<N, T> => self.length >= n)
286
406
 
287
407
  /**
288
- * Tests if a value is `truthy`.
408
+ * A predicate that checks if a value is "truthy" in JavaScript.
409
+ * Fails for `false`, `0`, `-0`, `0n`, `""`, `null`, `undefined`, and `NaN`.
289
410
  *
290
411
  * @example
291
412
  * ```ts
292
413
  * import * as assert from "node:assert"
293
414
  * import { isTruthy } from "effect/Predicate"
294
415
  *
295
- * assert.deepStrictEqual(isTruthy(1), true)
296
- * assert.deepStrictEqual(isTruthy(0), false)
297
- * assert.deepStrictEqual(isTruthy(""), false)
416
+ * assert.strictEqual(isTruthy(1), true)
417
+ * assert.strictEqual(isTruthy("hello"), true)
418
+ * assert.strictEqual(isTruthy({}), true)
419
+ *
420
+ * assert.strictEqual(isTruthy(0), false)
421
+ * assert.strictEqual(isTruthy(""), false)
422
+ * assert.strictEqual(isTruthy(null), false)
423
+ * assert.strictEqual(isTruthy(undefined), false)
298
424
  * ```
299
425
  *
300
426
  * @category guards
@@ -303,18 +429,18 @@ export const isTupleOfAtLeast: {
303
429
  export const isTruthy = (input: unknown) => !!input
304
430
 
305
431
  /**
306
- * Tests if a value is a `Set`.
432
+ * A refinement that checks if a value is a `Set`.
307
433
  *
308
434
  * @example
309
435
  * ```ts
310
436
  * import * as assert from "node:assert"
311
437
  * import { isSet } from "effect/Predicate"
312
438
  *
313
- * assert.deepStrictEqual(isSet(new Set([1, 2])), true)
314
- * assert.deepStrictEqual(isSet(new Set()), true)
315
- * assert.deepStrictEqual(isSet({}), false)
316
- * assert.deepStrictEqual(isSet(null), false)
317
- * assert.deepStrictEqual(isSet(undefined), false)
439
+ * assert.strictEqual(isSet(new Set([1, 2])), true)
440
+ * assert.strictEqual(isSet(new Set()), true)
441
+ *
442
+ * assert.strictEqual(isSet({}), false)
443
+ * assert.strictEqual(isSet([1, 2]), false)
318
444
  * ```
319
445
  *
320
446
  * @category guards
@@ -323,17 +449,17 @@ export const isTruthy = (input: unknown) => !!input
323
449
  export const isSet = (input: unknown): input is Set<unknown> => input instanceof Set
324
450
 
325
451
  /**
326
- * Tests if a value is a `Map`.
452
+ * A refinement that checks if a value is a `Map`.
327
453
  *
328
454
  * @example
329
455
  * ```ts
330
456
  * import * as assert from "node:assert"
331
457
  * import { isMap } from "effect/Predicate"
332
458
  *
333
- * assert.deepStrictEqual(isMap(new Map()), true)
334
- * assert.deepStrictEqual(isMap({}), false)
335
- * assert.deepStrictEqual(isMap(null), false)
336
- * assert.deepStrictEqual(isMap(undefined), false)
459
+ * assert.strictEqual(isMap(new Map()), true)
460
+ *
461
+ * assert.strictEqual(isMap({}), false)
462
+ * assert.strictEqual(isMap(new Set()), false)
337
463
  * ```
338
464
  *
339
465
  * @category guards
@@ -342,16 +468,18 @@ export const isSet = (input: unknown): input is Set<unknown> => input instanceof
342
468
  export const isMap = (input: unknown): input is Map<unknown, unknown> => input instanceof Map
343
469
 
344
470
  /**
345
- * Tests if a value is a `string`.
471
+ * A refinement that checks if a value is a `string`.
346
472
  *
347
473
  * @example
348
474
  * ```ts
349
475
  * import * as assert from "node:assert"
350
476
  * import { isString } from "effect/Predicate"
351
477
  *
352
- * assert.deepStrictEqual(isString("a"), true)
478
+ * assert.strictEqual(isString("hello"), true)
479
+ * assert.strictEqual(isString(""), true)
353
480
  *
354
- * assert.deepStrictEqual(isString(1), false)
481
+ * assert.strictEqual(isString(123), false)
482
+ * assert.strictEqual(isString(null), false)
355
483
  * ```
356
484
  *
357
485
  * @category guards
@@ -360,16 +488,20 @@ export const isMap = (input: unknown): input is Map<unknown, unknown> => input i
360
488
  export const isString = (input: unknown): input is string => typeof input === "string"
361
489
 
362
490
  /**
363
- * Tests if a value is a `number`.
491
+ * A refinement that checks if a value is a `number`. Note that this
492
+ * check returns `false` for `NaN`.
364
493
  *
365
494
  * @example
366
495
  * ```ts
367
496
  * import * as assert from "node:assert"
368
497
  * import { isNumber } from "effect/Predicate"
369
498
  *
370
- * assert.deepStrictEqual(isNumber(2), true)
499
+ * assert.strictEqual(isNumber(123), true)
500
+ * assert.strictEqual(isNumber(0), true)
501
+ * assert.strictEqual(isNumber(-1.5), true)
371
502
  *
372
- * assert.deepStrictEqual(isNumber("2"), false)
503
+ * assert.strictEqual(isNumber("123"), false)
504
+ * assert.strictEqual(isNumber(NaN), false) // Special case: NaN is a number type but returns false
373
505
  * ```
374
506
  *
375
507
  * @category guards
@@ -378,16 +510,18 @@ export const isString = (input: unknown): input is string => typeof input === "s
378
510
  export const isNumber = (input: unknown): input is number => typeof input === "number"
379
511
 
380
512
  /**
381
- * Tests if a value is a `boolean`.
513
+ * A refinement that checks if a value is a `boolean`.
382
514
  *
383
515
  * @example
384
516
  * ```ts
385
517
  * import * as assert from "node:assert"
386
518
  * import { isBoolean } from "effect/Predicate"
387
519
  *
388
- * assert.deepStrictEqual(isBoolean(true), true)
520
+ * assert.strictEqual(isBoolean(true), true)
521
+ * assert.strictEqual(isBoolean(false), true)
389
522
  *
390
- * assert.deepStrictEqual(isBoolean("true"), false)
523
+ * assert.strictEqual(isBoolean("true"), false)
524
+ * assert.strictEqual(isBoolean(0), false)
391
525
  * ```
392
526
  *
393
527
  * @category guards
@@ -396,16 +530,17 @@ export const isNumber = (input: unknown): input is number => typeof input === "n
396
530
  export const isBoolean = (input: unknown): input is boolean => typeof input === "boolean"
397
531
 
398
532
  /**
399
- * Tests if a value is a `bigint`.
533
+ * A refinement that checks if a value is a `bigint`.
400
534
  *
401
535
  * @example
402
536
  * ```ts
403
537
  * import * as assert from "node:assert"
404
538
  * import { isBigInt } from "effect/Predicate"
405
539
  *
406
- * assert.deepStrictEqual(isBigInt(1n), true)
540
+ * assert.strictEqual(isBigInt(1n), true)
407
541
  *
408
- * assert.deepStrictEqual(isBigInt(1), false)
542
+ * assert.strictEqual(isBigInt(1), false)
543
+ * assert.strictEqual(isBigInt("1"), false)
409
544
  * ```
410
545
  *
411
546
  * @category guards
@@ -414,16 +549,16 @@ export const isBoolean = (input: unknown): input is boolean => typeof input ===
414
549
  export const isBigInt = (input: unknown): input is bigint => typeof input === "bigint"
415
550
 
416
551
  /**
417
- * Tests if a value is a `symbol`.
552
+ * A refinement that checks if a value is a `symbol`.
418
553
  *
419
554
  * @example
420
555
  * ```ts
421
556
  * import * as assert from "node:assert"
422
557
  * import { isSymbol } from "effect/Predicate"
423
558
  *
424
- * assert.deepStrictEqual(isSymbol(Symbol.for("a")), true)
559
+ * assert.strictEqual(isSymbol(Symbol.for("a")), true)
425
560
  *
426
- * assert.deepStrictEqual(isSymbol("a"), false)
561
+ * assert.strictEqual(isSymbol("a"), false)
427
562
  * ```
428
563
  *
429
564
  * @category guards
@@ -432,20 +567,24 @@ export const isBigInt = (input: unknown): input is bigint => typeof input === "b
432
567
  export const isSymbol = (input: unknown): input is symbol => typeof input === "symbol"
433
568
 
434
569
  // TODO: make public
435
- /** @internal */
570
+ /**
571
+ * A refinement that checks if a value is a valid `PropertyKey` (a `string`, `number`, or `symbol`).
572
+ * @internal
573
+ */
436
574
  export const isPropertyKey = (u: unknown): u is PropertyKey => isString(u) || isNumber(u) || isSymbol(u)
437
575
 
438
576
  /**
439
- * Tests if a value is a `function`.
577
+ * A refinement that checks if a value is a `Function`.
440
578
  *
441
579
  * @example
442
580
  * ```ts
443
581
  * import * as assert from "node:assert"
444
582
  * import { isFunction } from "effect/Predicate"
445
583
  *
446
- * assert.deepStrictEqual(isFunction(isFunction), true)
584
+ * assert.strictEqual(isFunction(() => {}), true)
585
+ * assert.strictEqual(isFunction(isFunction), true)
447
586
  *
448
- * assert.deepStrictEqual(isFunction("function"), false)
587
+ * assert.strictEqual(isFunction("function"), false)
449
588
  * ```
450
589
  *
451
590
  * @category guards
@@ -454,17 +593,17 @@ export const isPropertyKey = (u: unknown): u is PropertyKey => isString(u) || is
454
593
  export const isFunction: (input: unknown) => input is Function = isFunction_
455
594
 
456
595
  /**
457
- * Tests if a value is `undefined`.
596
+ * A refinement that checks if a value is `undefined`.
458
597
  *
459
598
  * @example
460
599
  * ```ts
461
600
  * import * as assert from "node:assert"
462
601
  * import { isUndefined } from "effect/Predicate"
463
602
  *
464
- * assert.deepStrictEqual(isUndefined(undefined), true)
603
+ * assert.strictEqual(isUndefined(undefined), true)
465
604
  *
466
- * assert.deepStrictEqual(isUndefined(null), false)
467
- * assert.deepStrictEqual(isUndefined("undefined"), false)
605
+ * assert.strictEqual(isUndefined(null), false)
606
+ * assert.strictEqual(isUndefined("undefined"), false)
468
607
  * ```
469
608
  *
470
609
  * @category guards
@@ -473,17 +612,17 @@ export const isFunction: (input: unknown) => input is Function = isFunction_
473
612
  export const isUndefined = (input: unknown): input is undefined => input === undefined
474
613
 
475
614
  /**
476
- * Tests if a value is not `undefined`.
615
+ * A refinement that checks if a value is not `undefined`.
477
616
  *
478
617
  * @example
479
618
  * ```ts
480
619
  * import * as assert from "node:assert"
481
620
  * import { isNotUndefined } from "effect/Predicate"
482
621
  *
483
- * assert.deepStrictEqual(isNotUndefined(null), true)
484
- * assert.deepStrictEqual(isNotUndefined("undefined"), true)
622
+ * assert.strictEqual(isNotUndefined(null), true)
623
+ * assert.strictEqual(isNotUndefined("value"), true)
485
624
  *
486
- * assert.deepStrictEqual(isNotUndefined(undefined), false)
625
+ * assert.strictEqual(isNotUndefined(undefined), false)
487
626
  * ```
488
627
  *
489
628
  * @category guards
@@ -492,17 +631,17 @@ export const isUndefined = (input: unknown): input is undefined => input === und
492
631
  export const isNotUndefined = <A>(input: A): input is Exclude<A, undefined> => input !== undefined
493
632
 
494
633
  /**
495
- * Tests if a value is `null`.
634
+ * A refinement that checks if a value is `null`.
496
635
  *
497
636
  * @example
498
637
  * ```ts
499
638
  * import * as assert from "node:assert"
500
639
  * import { isNull } from "effect/Predicate"
501
640
  *
502
- * assert.deepStrictEqual(isNull(null), true)
641
+ * assert.strictEqual(isNull(null), true)
503
642
  *
504
- * assert.deepStrictEqual(isNull(undefined), false)
505
- * assert.deepStrictEqual(isNull("null"), false)
643
+ * assert.strictEqual(isNull(undefined), false)
644
+ * assert.strictEqual(isNull("null"), false)
506
645
  * ```
507
646
  *
508
647
  * @category guards
@@ -511,17 +650,17 @@ export const isNotUndefined = <A>(input: A): input is Exclude<A, undefined> => i
511
650
  export const isNull = (input: unknown): input is null => input === null
512
651
 
513
652
  /**
514
- * Tests if a value is not `null`.
653
+ * A refinement that checks if a value is not `null`.
515
654
  *
516
655
  * @example
517
656
  * ```ts
518
657
  * import * as assert from "node:assert"
519
658
  * import { isNotNull } from "effect/Predicate"
520
659
  *
521
- * assert.deepStrictEqual(isNotNull(undefined), true)
522
- * assert.deepStrictEqual(isNotNull("null"), true)
660
+ * assert.strictEqual(isNotNull(undefined), true)
661
+ * assert.strictEqual(isNotNull("value"), true)
523
662
  *
524
- * assert.deepStrictEqual(isNotNull(null), false)
663
+ * assert.strictEqual(isNotNull(null), false)
525
664
  * ```
526
665
  *
527
666
  * @category guards
@@ -530,17 +669,16 @@ export const isNull = (input: unknown): input is null => input === null
530
669
  export const isNotNull = <A>(input: A): input is Exclude<A, null> => input !== null
531
670
 
532
671
  /**
533
- * A guard that always fails.
672
+ * A refinement that always returns `false`. The type is narrowed to `never`.
534
673
  *
535
674
  * @example
536
675
  * ```ts
537
676
  * import * as assert from "node:assert"
538
677
  * import { isNever } from "effect/Predicate"
539
678
  *
540
- * assert.deepStrictEqual(isNever(null), false)
541
- * assert.deepStrictEqual(isNever(undefined), false)
542
- * assert.deepStrictEqual(isNever({}), false)
543
- * assert.deepStrictEqual(isNever([]), false)
679
+ * assert.strictEqual(isNever(1), false)
680
+ * assert.strictEqual(isNever(null), false)
681
+ * assert.strictEqual(isNever({}), false)
544
682
  * ```
545
683
  *
546
684
  * @category guards
@@ -549,18 +687,16 @@ export const isNotNull = <A>(input: A): input is Exclude<A, null> => input !== n
549
687
  export const isNever: (input: unknown) => input is never = (_: unknown): _ is never => false
550
688
 
551
689
  /**
552
- * A guard that always succeeds.
690
+ * A refinement that always returns `true`. The type is narrowed to `unknown`.
553
691
  *
554
692
  * @example
555
693
  * ```ts
556
694
  * import * as assert from "node:assert"
557
695
  * import { isUnknown } from "effect/Predicate"
558
696
  *
559
- * assert.deepStrictEqual(isUnknown(null), true)
560
- * assert.deepStrictEqual(isUnknown(undefined), true)
561
- *
562
- * assert.deepStrictEqual(isUnknown({}), true)
563
- * assert.deepStrictEqual(isUnknown([]), true)
697
+ * assert.strictEqual(isUnknown(1), true)
698
+ * assert.strictEqual(isUnknown(null), true)
699
+ * assert.strictEqual(isUnknown({}), true)
564
700
  * ```
565
701
  *
566
702
  * @category guards
@@ -568,46 +704,100 @@ export const isNever: (input: unknown) => input is never = (_: unknown): _ is ne
568
704
  */
569
705
  export const isUnknown: (input: unknown) => input is unknown = (_): _ is unknown => true
570
706
 
571
- /** @internal */
707
+ /**
708
+ * Checks if the input is an object or an array.
709
+ * @internal
710
+ */
572
711
  export const isRecordOrArray = (input: unknown): input is { [x: PropertyKey]: unknown } =>
573
712
  typeof input === "object" && input !== null
574
713
 
575
714
  /**
576
- * Tests if a value is an `object`.
715
+ * A refinement that checks if a value is an `object`. Note that in JavaScript,
716
+ * arrays and functions are also considered objects.
577
717
  *
578
718
  * @example
579
719
  * ```ts
580
720
  * import * as assert from "node:assert"
581
721
  * import { isObject } from "effect/Predicate"
582
722
  *
583
- * assert.deepStrictEqual(isObject({}), true)
584
- * assert.deepStrictEqual(isObject([]), true)
723
+ * assert.strictEqual(isObject({}), true)
724
+ * assert.strictEqual(isObject([]), true)
725
+ * assert.strictEqual(isObject(() => {}), true)
585
726
  *
586
- * assert.deepStrictEqual(isObject(null), false)
587
- * assert.deepStrictEqual(isObject(undefined), false)
727
+ * assert.strictEqual(isObject(null), false)
728
+ * assert.strictEqual(isObject("hello"), false)
588
729
  * ```
589
730
  *
590
731
  * @category guards
591
732
  * @since 2.0.0
733
+ * @see isRecord to check for plain objects (excluding arrays and functions).
592
734
  */
593
735
  export const isObject = (input: unknown): input is object => isRecordOrArray(input) || isFunction(input)
594
736
 
595
737
  /**
596
- * Checks whether a value is an `object` containing a specified property key.
738
+ * A refinement that checks if a value is an object-like value and has a specific property key.
739
+ *
740
+ * @example
741
+ * ```ts
742
+ * import * as assert from "node:assert"
743
+ * import { hasProperty } from "effect/Predicate"
744
+ *
745
+ * assert.strictEqual(hasProperty({ a: 1 }, "a"), true)
746
+ * assert.strictEqual(hasProperty({ a: 1 }, "b"), false)
747
+ *
748
+ * const value: unknown = { name: "Alice" };
749
+ * if (hasProperty(value, "name")) {
750
+ * // The type of `value` is narrowed to `{ name: unknown }`
751
+ * // and we can safely access `value.name`
752
+ * console.log(value.name)
753
+ * }
754
+ * ```
597
755
  *
598
756
  * @category guards
599
757
  * @since 2.0.0
600
758
  */
601
759
  export const hasProperty: {
602
760
  /**
603
- * Checks whether a value is an `object` containing a specified property key.
761
+ * A refinement that checks if a value is an object-like value and has a specific property key.
762
+ *
763
+ * @example
764
+ * ```ts
765
+ * import * as assert from "node:assert"
766
+ * import { hasProperty } from "effect/Predicate"
767
+ *
768
+ * assert.strictEqual(hasProperty({ a: 1 }, "a"), true)
769
+ * assert.strictEqual(hasProperty({ a: 1 }, "b"), false)
770
+ *
771
+ * const value: unknown = { name: "Alice" };
772
+ * if (hasProperty(value, "name")) {
773
+ * // The type of `value` is narrowed to `{ name: unknown }`
774
+ * // and we can safely access `value.name`
775
+ * console.log(value.name)
776
+ * }
777
+ * ```
604
778
  *
605
779
  * @category guards
606
780
  * @since 2.0.0
607
781
  */
608
782
  <P extends PropertyKey>(property: P): (self: unknown) => self is { [K in P]: unknown }
609
783
  /**
610
- * Checks whether a value is an `object` containing a specified property key.
784
+ * A refinement that checks if a value is an object-like value and has a specific property key.
785
+ *
786
+ * @example
787
+ * ```ts
788
+ * import * as assert from "node:assert"
789
+ * import { hasProperty } from "effect/Predicate"
790
+ *
791
+ * assert.strictEqual(hasProperty({ a: 1 }, "a"), true)
792
+ * assert.strictEqual(hasProperty({ a: 1 }, "b"), false)
793
+ *
794
+ * const value: unknown = { name: "Alice" };
795
+ * if (hasProperty(value, "name")) {
796
+ * // The type of `value` is narrowed to `{ name: unknown }`
797
+ * // and we can safely access `value.name`
798
+ * console.log(value.name)
799
+ * }
800
+ * ```
611
801
  *
612
802
  * @category guards
613
803
  * @since 2.0.0
@@ -620,19 +810,29 @@ export const hasProperty: {
620
810
  )
621
811
 
622
812
  /**
623
- * Tests if a value is an `object` with a property `_tag` that matches the given tag.
813
+ * A refinement that checks if a value is an object with a `_tag` property
814
+ * that matches the given tag. This is a powerful tool for working with
815
+ * discriminated union types.
624
816
  *
625
817
  * @example
626
818
  * ```ts
627
819
  * import * as assert from "node:assert"
628
820
  * import { isTagged } from "effect/Predicate"
629
821
  *
630
- * assert.deepStrictEqual(isTagged(1, "a"), false)
631
- * assert.deepStrictEqual(isTagged(null, "a"), false)
632
- * assert.deepStrictEqual(isTagged({}, "a"), false)
633
- * assert.deepStrictEqual(isTagged({ a: "a" }, "a"), false)
634
- * assert.deepStrictEqual(isTagged({ _tag: "a" }, "a"), true)
635
- * assert.deepStrictEqual(isTagged("a")({ _tag: "a" }), true)
822
+ * type Shape = { _tag: "circle"; radius: number } | { _tag: "square"; side: number }
823
+ *
824
+ * const isCircle = isTagged("circle")
825
+ *
826
+ * const shape1: Shape = { _tag: "circle", radius: 10 }
827
+ * const shape2: Shape = { _tag: "square", side: 5 }
828
+ *
829
+ * assert.strictEqual(isCircle(shape1), true)
830
+ * assert.strictEqual(isCircle(shape2), false)
831
+ *
832
+ * if (isCircle(shape1)) {
833
+ * // shape1 is now narrowed to { _tag: "circle"; radius: number }
834
+ * assert.strictEqual(shape1.radius, 10)
835
+ * }
636
836
  * ```
637
837
  *
638
838
  * @category guards
@@ -640,19 +840,29 @@ export const hasProperty: {
640
840
  */
641
841
  export const isTagged: {
642
842
  /**
643
- * Tests if a value is an `object` with a property `_tag` that matches the given tag.
843
+ * A refinement that checks if a value is an object with a `_tag` property
844
+ * that matches the given tag. This is a powerful tool for working with
845
+ * discriminated union types.
644
846
  *
645
847
  * @example
646
848
  * ```ts
647
849
  * import * as assert from "node:assert"
648
850
  * import { isTagged } from "effect/Predicate"
649
851
  *
650
- * assert.deepStrictEqual(isTagged(1, "a"), false)
651
- * assert.deepStrictEqual(isTagged(null, "a"), false)
652
- * assert.deepStrictEqual(isTagged({}, "a"), false)
653
- * assert.deepStrictEqual(isTagged({ a: "a" }, "a"), false)
654
- * assert.deepStrictEqual(isTagged({ _tag: "a" }, "a"), true)
655
- * assert.deepStrictEqual(isTagged("a")({ _tag: "a" }), true)
852
+ * type Shape = { _tag: "circle"; radius: number } | { _tag: "square"; side: number }
853
+ *
854
+ * const isCircle = isTagged("circle")
855
+ *
856
+ * const shape1: Shape = { _tag: "circle", radius: 10 }
857
+ * const shape2: Shape = { _tag: "square", side: 5 }
858
+ *
859
+ * assert.strictEqual(isCircle(shape1), true)
860
+ * assert.strictEqual(isCircle(shape2), false)
861
+ *
862
+ * if (isCircle(shape1)) {
863
+ * // shape1 is now narrowed to { _tag: "circle"; radius: number }
864
+ * assert.strictEqual(shape1.radius, 10)
865
+ * }
656
866
  * ```
657
867
  *
658
868
  * @category guards
@@ -660,19 +870,29 @@ export const isTagged: {
660
870
  */
661
871
  <K extends string>(tag: K): (self: unknown) => self is { _tag: K }
662
872
  /**
663
- * Tests if a value is an `object` with a property `_tag` that matches the given tag.
873
+ * A refinement that checks if a value is an object with a `_tag` property
874
+ * that matches the given tag. This is a powerful tool for working with
875
+ * discriminated union types.
664
876
  *
665
877
  * @example
666
878
  * ```ts
667
879
  * import * as assert from "node:assert"
668
880
  * import { isTagged } from "effect/Predicate"
669
881
  *
670
- * assert.deepStrictEqual(isTagged(1, "a"), false)
671
- * assert.deepStrictEqual(isTagged(null, "a"), false)
672
- * assert.deepStrictEqual(isTagged({}, "a"), false)
673
- * assert.deepStrictEqual(isTagged({ a: "a" }, "a"), false)
674
- * assert.deepStrictEqual(isTagged({ _tag: "a" }, "a"), true)
675
- * assert.deepStrictEqual(isTagged("a")({ _tag: "a" }), true)
882
+ * type Shape = { _tag: "circle"; radius: number } | { _tag: "square"; side: number }
883
+ *
884
+ * const isCircle = isTagged("circle")
885
+ *
886
+ * const shape1: Shape = { _tag: "circle", radius: 10 }
887
+ * const shape2: Shape = { _tag: "square", side: 5 }
888
+ *
889
+ * assert.strictEqual(isCircle(shape1), true)
890
+ * assert.strictEqual(isCircle(shape2), false)
891
+ *
892
+ * if (isCircle(shape1)) {
893
+ * // shape1 is now narrowed to { _tag: "circle"; radius: number }
894
+ * assert.strictEqual(shape1.radius, 10)
895
+ * }
676
896
  * ```
677
897
  *
678
898
  * @category guards
@@ -685,57 +905,61 @@ export const isTagged: {
685
905
  )
686
906
 
687
907
  /**
688
- * A guard that succeeds when the input is `null` or `undefined`.
908
+ * A refinement that checks if a value is either `null` or `undefined`.
689
909
  *
690
910
  * @example
691
911
  * ```ts
692
912
  * import * as assert from "node:assert"
693
913
  * import { isNullable } from "effect/Predicate"
694
914
  *
695
- * assert.deepStrictEqual(isNullable(null), true)
696
- * assert.deepStrictEqual(isNullable(undefined), true)
915
+ * assert.strictEqual(isNullable(null), true)
916
+ * assert.strictEqual(isNullable(undefined), true)
697
917
  *
698
- * assert.deepStrictEqual(isNullable({}), false)
699
- * assert.deepStrictEqual(isNullable([]), false)
918
+ * assert.strictEqual(isNullable(0), false)
919
+ * assert.strictEqual(isNullable(""), false)
700
920
  * ```
701
921
  *
702
922
  * @category guards
703
923
  * @since 2.0.0
924
+ * @see isNotNullable
704
925
  */
705
926
  export const isNullable = <A>(input: A): input is Extract<A, null | undefined> => input === null || input === undefined
706
927
 
707
928
  /**
708
- * A guard that succeeds when the input is not `null` or `undefined`.
929
+ * A refinement that checks if a value is neither `null` nor `undefined`.
930
+ * The type is narrowed to `NonNullable<A>`.
709
931
  *
710
932
  * @example
711
933
  * ```ts
712
934
  * import * as assert from "node:assert"
713
935
  * import { isNotNullable } from "effect/Predicate"
714
936
  *
715
- * assert.deepStrictEqual(isNotNullable({}), true)
716
- * assert.deepStrictEqual(isNotNullable([]), true)
937
+ * assert.strictEqual(isNotNullable(0), true)
938
+ * assert.strictEqual(isNotNullable("hello"), true)
717
939
  *
718
- * assert.deepStrictEqual(isNotNullable(null), false)
719
- * assert.deepStrictEqual(isNotNullable(undefined), false)
940
+ * assert.strictEqual(isNotNullable(null), false)
941
+ * assert.strictEqual(isNotNullable(undefined), false)
720
942
  * ```
721
943
  *
722
944
  * @category guards
723
945
  * @since 2.0.0
946
+ * @see isNullable
724
947
  */
725
948
  export const isNotNullable = <A>(input: A): input is NonNullable<A> => input !== null && input !== undefined
726
949
 
727
950
  /**
728
- * A guard that succeeds when the input is an `Error`.
951
+ * A refinement that checks if a value is an instance of `Error`.
729
952
  *
730
953
  * @example
731
954
  * ```ts
732
955
  * import * as assert from "node:assert"
733
956
  * import { isError } from "effect/Predicate"
734
957
  *
735
- * assert.deepStrictEqual(isError(new Error()), true)
958
+ * assert.strictEqual(isError(new Error("boom")), true)
959
+ * assert.strictEqual(isError(new TypeError("boom")), true)
736
960
  *
737
- * assert.deepStrictEqual(isError(null), false)
738
- * assert.deepStrictEqual(isError({}), false)
961
+ * assert.strictEqual(isError({ message: "boom" }), false)
962
+ * assert.strictEqual(isError("boom"), false)
739
963
  * ```
740
964
  *
741
965
  * @category guards
@@ -744,17 +968,17 @@ export const isNotNullable = <A>(input: A): input is NonNullable<A> => input !==
744
968
  export const isError = (input: unknown): input is Error => input instanceof Error
745
969
 
746
970
  /**
747
- * A guard that succeeds when the input is a `Uint8Array`.
971
+ * A refinement that checks if a value is a `Uint8Array`.
748
972
  *
749
973
  * @example
750
974
  * ```ts
751
975
  * import * as assert from "node:assert"
752
976
  * import { isUint8Array } from "effect/Predicate"
753
977
  *
754
- * assert.deepStrictEqual(isUint8Array(new Uint8Array()), true)
978
+ * assert.strictEqual(isUint8Array(new Uint8Array()), true)
755
979
  *
756
- * assert.deepStrictEqual(isUint8Array(null), false)
757
- * assert.deepStrictEqual(isUint8Array({}), false)
980
+ * assert.strictEqual(isUint8Array(new Uint16Array()), false)
981
+ * assert.strictEqual(isUint8Array([1, 2, 3]), false)
758
982
  * ```
759
983
  *
760
984
  * @category guards
@@ -763,17 +987,17 @@ export const isError = (input: unknown): input is Error => input instanceof Erro
763
987
  export const isUint8Array = (input: unknown): input is Uint8Array => input instanceof Uint8Array
764
988
 
765
989
  /**
766
- * A guard that succeeds when the input is a `Date`.
990
+ * A refinement that checks if a value is a `Date` object.
767
991
  *
768
992
  * @example
769
993
  * ```ts
770
994
  * import * as assert from "node:assert"
771
995
  * import { isDate } from "effect/Predicate"
772
996
  *
773
- * assert.deepStrictEqual(isDate(new Date()), true)
997
+ * assert.strictEqual(isDate(new Date()), true)
774
998
  *
775
- * assert.deepStrictEqual(isDate(null), false)
776
- * assert.deepStrictEqual(isDate({}), false)
999
+ * assert.strictEqual(isDate(Date.now()), false) // `Date.now()` returns a number
1000
+ * assert.strictEqual(isDate("2023-01-01"), false)
777
1001
  * ```
778
1002
  *
779
1003
  * @category guards
@@ -782,18 +1006,20 @@ export const isUint8Array = (input: unknown): input is Uint8Array => input insta
782
1006
  export const isDate = (input: unknown): input is Date => input instanceof Date
783
1007
 
784
1008
  /**
785
- * A guard that succeeds when the input is an `Iterable`.
1009
+ * A refinement that checks if a value is an `Iterable`.
1010
+ * Many built-in types are iterable, such as `Array`, `string`, `Map`, and `Set`.
786
1011
  *
787
1012
  * @example
788
1013
  * ```ts
789
1014
  * import * as assert from "node:assert"
790
1015
  * import { isIterable } from "effect/Predicate"
791
1016
  *
792
- * assert.deepStrictEqual(isIterable([]), true)
793
- * assert.deepStrictEqual(isIterable(new Set()), true)
1017
+ * assert.strictEqual(isIterable([]), true)
1018
+ * assert.strictEqual(isIterable("hello"), true)
1019
+ * assert.strictEqual(isIterable(new Set()), true)
794
1020
  *
795
- * assert.deepStrictEqual(isIterable(null), false)
796
- * assert.deepStrictEqual(isIterable({}), false)
1021
+ * assert.strictEqual(isIterable({}), false)
1022
+ * assert.strictEqual(isIterable(123), false)
797
1023
  * ```
798
1024
  *
799
1025
  * @category guards
@@ -802,44 +1028,46 @@ export const isDate = (input: unknown): input is Date => input instanceof Date
802
1028
  export const isIterable = (input: unknown): input is Iterable<unknown> => hasProperty(input, Symbol.iterator)
803
1029
 
804
1030
  /**
805
- * A guard that succeeds when the input is a record.
1031
+ * A refinement that checks if a value is a record (i.e., a plain object).
1032
+ * This check returns `false` for arrays, `null`, and functions.
806
1033
  *
807
1034
  * @example
808
1035
  * ```ts
809
1036
  * import * as assert from "node:assert"
810
1037
  * import { isRecord } from "effect/Predicate"
811
1038
  *
812
- * assert.deepStrictEqual(isRecord({}), true)
813
- * assert.deepStrictEqual(isRecord({ a: 1 }), true)
1039
+ * assert.strictEqual(isRecord({}), true)
1040
+ * assert.strictEqual(isRecord({ a: 1 }), true)
814
1041
  *
815
- * assert.deepStrictEqual(isRecord([]), false)
816
- * assert.deepStrictEqual(isRecord([1, 2, 3]), false)
817
- * assert.deepStrictEqual(isRecord(null), false)
818
- * assert.deepStrictEqual(isRecord(undefined), false)
819
- * assert.deepStrictEqual(isRecord(() => null), false)
1042
+ * assert.strictEqual(isRecord([]), false)
1043
+ * assert.strictEqual(isRecord(new Date()), false)
1044
+ * assert.strictEqual(isRecord(null), false)
1045
+ * assert.strictEqual(isRecord(() => null), false)
820
1046
  * ```
821
1047
  *
822
1048
  * @category guards
823
1049
  * @since 2.0.0
1050
+ * @see isObject
824
1051
  */
825
1052
  export const isRecord = (input: unknown): input is { [x: string | symbol]: unknown } =>
826
1053
  isRecordOrArray(input) && !Array.isArray(input)
827
1054
 
828
1055
  /**
829
- * A guard that succeeds when the input is a readonly record.
1056
+ * A refinement that checks if a value is a readonly record (i.e., a plain object).
1057
+ * This check returns `false` for arrays, `null`, and functions.
1058
+ *
1059
+ * This is an alias for `isRecord`.
830
1060
  *
831
1061
  * @example
832
1062
  * ```ts
833
1063
  * import * as assert from "node:assert"
834
1064
  * import { isReadonlyRecord } from "effect/Predicate"
835
1065
  *
836
- * assert.deepStrictEqual(isReadonlyRecord({}), true)
837
- * assert.deepStrictEqual(isReadonlyRecord({ a: 1 }), true)
1066
+ * assert.strictEqual(isReadonlyRecord({}), true)
1067
+ * assert.strictEqual(isReadonlyRecord({ a: 1 }), true)
838
1068
  *
839
- * assert.deepStrictEqual(isReadonlyRecord([]), false)
840
- * assert.deepStrictEqual(isReadonlyRecord([1, 2, 3]), false)
841
- * assert.deepStrictEqual(isReadonlyRecord(null), false)
842
- * assert.deepStrictEqual(isReadonlyRecord(undefined), false)
1069
+ * assert.strictEqual(isReadonlyRecord([]), false)
1070
+ * assert.strictEqual(isReadonlyRecord(null), false)
843
1071
  * ```
844
1072
  *
845
1073
  * @category guards
@@ -850,19 +1078,24 @@ export const isReadonlyRecord: (
850
1078
  ) => input is { readonly [x: string | symbol]: unknown } = isRecord
851
1079
 
852
1080
  /**
853
- * A guard that succeeds when the input is a Promise.
1081
+ * A refinement that checks if a value is a `Promise`. It performs a duck-typing check
1082
+ * for `.then` and `.catch` methods.
854
1083
  *
855
1084
  * @example
856
1085
  * ```ts
857
1086
  * import * as assert from "node:assert"
858
1087
  * import { isPromise } from "effect/Predicate"
859
1088
  *
860
- * assert.deepStrictEqual(isPromise({}), false)
861
- * assert.deepStrictEqual(isPromise(Promise.resolve("hello")), true)
1089
+ * assert.strictEqual(isPromise(Promise.resolve(1)), true)
1090
+ * assert.strictEqual(isPromise(new Promise(() => {})), true)
1091
+ *
1092
+ * assert.strictEqual(isPromise({ then() {} }), false) // Missing .catch
1093
+ * assert.strictEqual(isPromise({}), false)
862
1094
  * ```
863
1095
  *
864
1096
  * @category guards
865
1097
  * @since 2.0.0
1098
+ * @see isPromiseLike
866
1099
  */
867
1100
  export const isPromise = (
868
1101
  input: unknown
@@ -870,23 +1103,40 @@ export const isPromise = (
870
1103
  hasProperty(input, "then") && "catch" in input && isFunction(input.then) && isFunction(input.catch)
871
1104
 
872
1105
  /**
1106
+ * A refinement that checks if a value is `PromiseLike`. It performs a duck-typing
1107
+ * check for a `.then` method.
1108
+ *
1109
+ * @example
1110
+ * ```ts
1111
+ * import * as assert from "node:assert"
1112
+ * import { isPromiseLike } from "effect/Predicate"
1113
+ *
1114
+ * assert.strictEqual(isPromiseLike(Promise.resolve(1)), true)
1115
+ * assert.strictEqual(isPromiseLike({ then: () => {} }), true)
1116
+ *
1117
+ * assert.strictEqual(isPromiseLike({}), false)
1118
+ * ```
1119
+ *
873
1120
  * @category guards
874
1121
  * @since 2.0.0
1122
+ * @see isPromise
875
1123
  */
876
1124
  export const isPromiseLike = (
877
1125
  input: unknown
878
1126
  ): input is PromiseLike<unknown> => hasProperty(input, "then") && isFunction(input.then)
879
1127
 
880
1128
  /**
881
- * Tests if a value is a `RegExp`.
1129
+ * A refinement that checks if a value is a `RegExp`.
882
1130
  *
883
1131
  * @example
884
1132
  * ```ts
885
1133
  * import * as assert from "node:assert"
886
1134
  * import { Predicate } from "effect"
887
1135
  *
888
- * assert.deepStrictEqual(Predicate.isRegExp(/a/), true)
889
- * assert.deepStrictEqual(Predicate.isRegExp("a"), false)
1136
+ * assert.strictEqual(Predicate.isRegExp(/a/), true)
1137
+ * assert.strictEqual(Predicate.isRegExp(new RegExp("a")), true)
1138
+ *
1139
+ * assert.strictEqual(Predicate.isRegExp("/a/"), false)
890
1140
  * ```
891
1141
  *
892
1142
  * @category guards
@@ -895,22 +1145,157 @@ export const isPromiseLike = (
895
1145
  export const isRegExp = (input: unknown): input is RegExp => input instanceof RegExp
896
1146
 
897
1147
  /**
1148
+ * Composes a `Refinement` with another `Refinement` or `Predicate`.
1149
+ *
1150
+ * This can be used to chain checks. The first refinement is applied, and if it
1151
+ * passes, the second check is applied to the same value, potentially refining
1152
+ * the type further.
1153
+ *
1154
+ * @example
1155
+ * ```ts
1156
+ * import { Predicate } from "effect"
1157
+ * import * as assert from "node:assert"
1158
+ *
1159
+ * const isString = (u: unknown): u is string => typeof u === "string"
1160
+ * const minLength = (n: number) => (s: string): boolean => s.length >= n
1161
+ *
1162
+ * // Create a refinement that checks for a string with a minimum length of 3
1163
+ * const isLongString = Predicate.compose(isString, minLength(3))
1164
+ *
1165
+ * let value: unknown = "hello"
1166
+ *
1167
+ * assert.strictEqual(isLongString(value), true)
1168
+ * if (isLongString(value)) {
1169
+ * // value is narrowed to string
1170
+ * assert.strictEqual(value.toUpperCase(), "HELLO")
1171
+ * }
1172
+ * assert.strictEqual(isLongString("hi"), false)
1173
+ * ```
1174
+ *
898
1175
  * @since 2.0.0
899
1176
  */
900
1177
  export const compose: {
901
1178
  /**
1179
+ * Composes a `Refinement` with another `Refinement` or `Predicate`.
1180
+ *
1181
+ * This can be used to chain checks. The first refinement is applied, and if it
1182
+ * passes, the second check is applied to the same value, potentially refining
1183
+ * the type further.
1184
+ *
1185
+ * @example
1186
+ * ```ts
1187
+ * import { Predicate } from "effect"
1188
+ * import * as assert from "node:assert"
1189
+ *
1190
+ * const isString = (u: unknown): u is string => typeof u === "string"
1191
+ * const minLength = (n: number) => (s: string): boolean => s.length >= n
1192
+ *
1193
+ * // Create a refinement that checks for a string with a minimum length of 3
1194
+ * const isLongString = Predicate.compose(isString, minLength(3))
1195
+ *
1196
+ * let value: unknown = "hello"
1197
+ *
1198
+ * assert.strictEqual(isLongString(value), true)
1199
+ * if (isLongString(value)) {
1200
+ * // value is narrowed to string
1201
+ * assert.strictEqual(value.toUpperCase(), "HELLO")
1202
+ * }
1203
+ * assert.strictEqual(isLongString("hi"), false)
1204
+ * ```
1205
+ *
902
1206
  * @since 2.0.0
903
1207
  */
904
1208
  <A, B extends A, C extends B, D extends C>(bc: Refinement<C, D>): (ab: Refinement<A, B>) => Refinement<A, D>
905
1209
  /**
1210
+ * Composes a `Refinement` with another `Refinement` or `Predicate`.
1211
+ *
1212
+ * This can be used to chain checks. The first refinement is applied, and if it
1213
+ * passes, the second check is applied to the same value, potentially refining
1214
+ * the type further.
1215
+ *
1216
+ * @example
1217
+ * ```ts
1218
+ * import { Predicate } from "effect"
1219
+ * import * as assert from "node:assert"
1220
+ *
1221
+ * const isString = (u: unknown): u is string => typeof u === "string"
1222
+ * const minLength = (n: number) => (s: string): boolean => s.length >= n
1223
+ *
1224
+ * // Create a refinement that checks for a string with a minimum length of 3
1225
+ * const isLongString = Predicate.compose(isString, minLength(3))
1226
+ *
1227
+ * let value: unknown = "hello"
1228
+ *
1229
+ * assert.strictEqual(isLongString(value), true)
1230
+ * if (isLongString(value)) {
1231
+ * // value is narrowed to string
1232
+ * assert.strictEqual(value.toUpperCase(), "HELLO")
1233
+ * }
1234
+ * assert.strictEqual(isLongString("hi"), false)
1235
+ * ```
1236
+ *
906
1237
  * @since 2.0.0
907
1238
  */
908
1239
  <A, B extends A>(bc: Predicate<NoInfer<B>>): (ab: Refinement<A, B>) => Refinement<A, B>
909
1240
  /**
1241
+ * Composes a `Refinement` with another `Refinement` or `Predicate`.
1242
+ *
1243
+ * This can be used to chain checks. The first refinement is applied, and if it
1244
+ * passes, the second check is applied to the same value, potentially refining
1245
+ * the type further.
1246
+ *
1247
+ * @example
1248
+ * ```ts
1249
+ * import { Predicate } from "effect"
1250
+ * import * as assert from "node:assert"
1251
+ *
1252
+ * const isString = (u: unknown): u is string => typeof u === "string"
1253
+ * const minLength = (n: number) => (s: string): boolean => s.length >= n
1254
+ *
1255
+ * // Create a refinement that checks for a string with a minimum length of 3
1256
+ * const isLongString = Predicate.compose(isString, minLength(3))
1257
+ *
1258
+ * let value: unknown = "hello"
1259
+ *
1260
+ * assert.strictEqual(isLongString(value), true)
1261
+ * if (isLongString(value)) {
1262
+ * // value is narrowed to string
1263
+ * assert.strictEqual(value.toUpperCase(), "HELLO")
1264
+ * }
1265
+ * assert.strictEqual(isLongString("hi"), false)
1266
+ * ```
1267
+ *
910
1268
  * @since 2.0.0
911
1269
  */
912
1270
  <A, B extends A, C extends B, D extends C>(ab: Refinement<A, B>, bc: Refinement<C, D>): Refinement<A, D>
913
1271
  /**
1272
+ * Composes a `Refinement` with another `Refinement` or `Predicate`.
1273
+ *
1274
+ * This can be used to chain checks. The first refinement is applied, and if it
1275
+ * passes, the second check is applied to the same value, potentially refining
1276
+ * the type further.
1277
+ *
1278
+ * @example
1279
+ * ```ts
1280
+ * import { Predicate } from "effect"
1281
+ * import * as assert from "node:assert"
1282
+ *
1283
+ * const isString = (u: unknown): u is string => typeof u === "string"
1284
+ * const minLength = (n: number) => (s: string): boolean => s.length >= n
1285
+ *
1286
+ * // Create a refinement that checks for a string with a minimum length of 3
1287
+ * const isLongString = Predicate.compose(isString, minLength(3))
1288
+ *
1289
+ * let value: unknown = "hello"
1290
+ *
1291
+ * assert.strictEqual(isLongString(value), true)
1292
+ * if (isLongString(value)) {
1293
+ * // value is narrowed to string
1294
+ * assert.strictEqual(value.toUpperCase(), "HELLO")
1295
+ * }
1296
+ * assert.strictEqual(isLongString("hi"), false)
1297
+ * ```
1298
+ *
914
1299
  * @since 2.0.0
915
1300
  */
916
1301
  <A, B extends A>(ab: Refinement<A, B>, bc: Predicate<NoInfer<B>>): Refinement<A, B>
@@ -921,6 +1306,9 @@ export const compose: {
921
1306
  )
922
1307
 
923
1308
  /**
1309
+ * Combines two predicates to test a tuple of two values. The first predicate tests the
1310
+ * first element of the tuple, and the second predicate tests the second element.
1311
+ *
924
1312
  * @category combining
925
1313
  * @since 2.0.0
926
1314
  */
@@ -929,8 +1317,14 @@ export const product =
929
1317
  ([a, b]) => self(a) && that(b)
930
1318
 
931
1319
  /**
1320
+ * Takes an iterable of predicates and returns a new predicate that tests an array of values.
1321
+ * The new predicate returns `true` if each predicate at a given index is satisfied by the
1322
+ * value at the same index in the array. The check stops at the length of the shorter of
1323
+ * the two iterables (predicates or values).
1324
+ *
932
1325
  * @category combining
933
1326
  * @since 2.0.0
1327
+ * @see tuple for a more powerful, variadic version.
934
1328
  */
935
1329
  export const all = <A>(
936
1330
  collection: Iterable<Predicate<A>>
@@ -951,6 +1345,9 @@ export const all = <A>(
951
1345
  }
952
1346
 
953
1347
  /**
1348
+ * Combines a predicate for a single value and an iterable of predicates for the rest of an array.
1349
+ * Useful for checking the head and tail of an array separately.
1350
+ *
954
1351
  * @category combining
955
1352
  * @since 2.0.0
956
1353
  */
@@ -963,24 +1360,66 @@ export const productMany = <A>(
963
1360
  }
964
1361
 
965
1362
  /**
966
- * Similar to `Promise.all` but operates on `Predicate`s.
1363
+ * Combines an array of predicates into a single predicate that tests an array of values.
1364
+ * This function is highly type-aware and will produce a `Refinement` if any of the provided
1365
+ * predicates are `Refinement`s, allowing for powerful type-narrowing of tuples.
967
1366
  *
968
- * ```ts skip-type-checking
969
- * [Refinement<A, B>, Refinement<C, D>, ...] -> Refinement<[A, C, ...], [B, D, ...]>
970
- * [Predicate<A>, Predicate<B>, ...] -> Predicate<[A, B, ...]>
971
- * [Refinement<A, B>, Predicate<C>, ...] -> Refinement<[A, C, ...], [B, C, ...]>
1367
+ * - If all predicates are `Predicate<T>`, the result is `Predicate<[T, T, ...]>`.
1368
+ * - If any predicate is a `Refinement<A, B>`, the result is a `Refinement` that narrows
1369
+ * the input tuple type to a more specific tuple type.
1370
+ *
1371
+ * @example
1372
+ * ```ts
1373
+ * import * as assert from "node:assert"
1374
+ * import { Predicate } from "effect"
1375
+ *
1376
+ * const isString = (u: unknown): u is string => typeof u === "string"
1377
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1378
+ *
1379
+ * // Create a refinement for a [string, number] tuple
1380
+ * const isStringNumberTuple = Predicate.tuple(isString, isNumber)
1381
+ *
1382
+ * const value: [unknown, unknown] = ["hello", 123]
1383
+ * if (isStringNumberTuple(value)) {
1384
+ * // value is narrowed to [string, number]
1385
+ * const [s, n] = value
1386
+ * assert.strictEqual(s.toUpperCase(), "HELLO")
1387
+ * assert.strictEqual(n.toFixed(2), "123.00")
1388
+ * }
1389
+ * assert.strictEqual(isStringNumberTuple(["hello", "123"]), false)
972
1390
  * ```
973
1391
  *
974
1392
  * @since 2.0.0
975
1393
  */
976
1394
  export const tuple: {
977
1395
  /**
978
- * Similar to `Promise.all` but operates on `Predicate`s.
1396
+ * Combines an array of predicates into a single predicate that tests an array of values.
1397
+ * This function is highly type-aware and will produce a `Refinement` if any of the provided
1398
+ * predicates are `Refinement`s, allowing for powerful type-narrowing of tuples.
1399
+ *
1400
+ * - If all predicates are `Predicate<T>`, the result is `Predicate<[T, T, ...]>`.
1401
+ * - If any predicate is a `Refinement<A, B>`, the result is a `Refinement` that narrows
1402
+ * the input tuple type to a more specific tuple type.
979
1403
  *
980
- * ```ts skip-type-checking
981
- * [Refinement<A, B>, Refinement<C, D>, ...] -> Refinement<[A, C, ...], [B, D, ...]>
982
- * [Predicate<A>, Predicate<B>, ...] -> Predicate<[A, B, ...]>
983
- * [Refinement<A, B>, Predicate<C>, ...] -> Refinement<[A, C, ...], [B, C, ...]>
1404
+ * @example
1405
+ * ```ts
1406
+ * import * as assert from "node:assert"
1407
+ * import { Predicate } from "effect"
1408
+ *
1409
+ * const isString = (u: unknown): u is string => typeof u === "string"
1410
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1411
+ *
1412
+ * // Create a refinement for a [string, number] tuple
1413
+ * const isStringNumberTuple = Predicate.tuple(isString, isNumber)
1414
+ *
1415
+ * const value: [unknown, unknown] = ["hello", 123]
1416
+ * if (isStringNumberTuple(value)) {
1417
+ * // value is narrowed to [string, number]
1418
+ * const [s, n] = value
1419
+ * assert.strictEqual(s.toUpperCase(), "HELLO")
1420
+ * assert.strictEqual(n.toFixed(2), "123.00")
1421
+ * }
1422
+ * assert.strictEqual(isStringNumberTuple(["hello", "123"]), false)
984
1423
  * ```
985
1424
  *
986
1425
  * @since 2.0.0
@@ -993,20 +1432,68 @@ export const tuple: {
993
1432
  } = (...elements: ReadonlyArray<Predicate.Any>) => all(elements) as any
994
1433
 
995
1434
  /**
996
- * ```ts skip-type-checking
997
- * { ab: Refinement<A, B>; cd: Refinement<C, D>, ... } -> Refinement<{ ab: A; cd: C; ... }, { ab: B; cd: D; ... }>
998
- * { a: Predicate<A, B>; b: Predicate<B>, ... } -> Predicate<{ a: A; b: B; ... }>
999
- * { ab: Refinement<A, B>; c: Predicate<C>, ... } -> Refinement<{ ab: A; c: C; ... }, { ab: B; c: С; ... }>
1435
+ * Combines a record of predicates into a single predicate that tests a record of values.
1436
+ * This function is highly type-aware and will produce a `Refinement` if any of the provided
1437
+ * predicates are `Refinement`s, allowing for powerful type-narrowing of structs.
1438
+ *
1439
+ * - If all predicates are `Predicate<T>`, the result is `Predicate<{ k: T, ... }>`.
1440
+ * - If any predicate is a `Refinement<A, B>`, the result is a `Refinement` that narrows
1441
+ * the input record type to a more specific record type.
1442
+ *
1443
+ * @example
1444
+ * ```ts
1445
+ * import * as assert from "node:assert"
1446
+ * import { Predicate } from "effect"
1447
+ *
1448
+ * const isString = (u: unknown): u is string => typeof u === "string"
1449
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1450
+ *
1451
+ * const personPredicate = Predicate.struct({
1452
+ * name: isString,
1453
+ * age: isNumber
1454
+ * })
1455
+ *
1456
+ * const value: { name: unknown; age: unknown } = { name: "Alice", age: 30 }
1457
+ * if (personPredicate(value)) {
1458
+ * // value is narrowed to { name: string; age: number }
1459
+ * assert.strictEqual(value.name.toUpperCase(), "ALICE")
1460
+ * assert.strictEqual(value.age.toFixed(0), "30")
1461
+ * }
1462
+ * assert.strictEqual(personPredicate({ name: "Bob", age: "40" }), false)
1000
1463
  * ```
1001
1464
  *
1002
1465
  * @since 2.0.0
1003
1466
  */
1004
1467
  export const struct: {
1005
1468
  /**
1006
- * ```ts skip-type-checking
1007
- * { ab: Refinement<A, B>; cd: Refinement<C, D>, ... } -> Refinement<{ ab: A; cd: C; ... }, { ab: B; cd: D; ... }>
1008
- * { a: Predicate<A, B>; b: Predicate<B>, ... } -> Predicate<{ a: A; b: B; ... }>
1009
- * { ab: Refinement<A, B>; c: Predicate<C>, ... } -> Refinement<{ ab: A; c: C; ... }, { ab: B; c: С; ... }>
1469
+ * Combines a record of predicates into a single predicate that tests a record of values.
1470
+ * This function is highly type-aware and will produce a `Refinement` if any of the provided
1471
+ * predicates are `Refinement`s, allowing for powerful type-narrowing of structs.
1472
+ *
1473
+ * - If all predicates are `Predicate<T>`, the result is `Predicate<{ k: T, ... }>`.
1474
+ * - If any predicate is a `Refinement<A, B>`, the result is a `Refinement` that narrows
1475
+ * the input record type to a more specific record type.
1476
+ *
1477
+ * @example
1478
+ * ```ts
1479
+ * import * as assert from "node:assert"
1480
+ * import { Predicate } from "effect"
1481
+ *
1482
+ * const isString = (u: unknown): u is string => typeof u === "string"
1483
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1484
+ *
1485
+ * const personPredicate = Predicate.struct({
1486
+ * name: isString,
1487
+ * age: isNumber
1488
+ * })
1489
+ *
1490
+ * const value: { name: unknown; age: unknown } = { name: "Alice", age: 30 }
1491
+ * if (personPredicate(value)) {
1492
+ * // value is narrowed to { name: string; age: number }
1493
+ * assert.strictEqual(value.name.toUpperCase(), "ALICE")
1494
+ * assert.strictEqual(value.age.toFixed(0), "30")
1495
+ * }
1496
+ * assert.strictEqual(personPredicate({ name: "Bob", age: "40" }), false)
1010
1497
  * ```
1011
1498
  *
1012
1499
  * @since 2.0.0
@@ -1030,18 +1517,21 @@ export const struct: {
1030
1517
  }) as any
1031
1518
 
1032
1519
  /**
1033
- * Negates the result of a given predicate.
1520
+ * Returns a new predicate that is the logical negation of the given predicate.
1521
+ *
1522
+ * **Note**: If the input is a `Refinement`, the resulting predicate will be a
1523
+ * simple `Predicate`, as TypeScript cannot infer the negative type.
1034
1524
  *
1035
1525
  * @example
1036
1526
  * ```ts
1037
1527
  * import * as assert from "node:assert"
1038
1528
  * import { Predicate, Number } from "effect"
1039
1529
  *
1040
- * const isPositive = Predicate.not(Number.lessThan(0))
1530
+ * const isNonPositive = Predicate.not(Number.greaterThan(0))
1041
1531
  *
1042
- * assert.deepStrictEqual(isPositive(-1), false)
1043
- * assert.deepStrictEqual(isPositive(0), true)
1044
- * assert.deepStrictEqual(isPositive(1), true)
1532
+ * assert.strictEqual(isNonPositive(-1), true)
1533
+ * assert.strictEqual(isNonPositive(0), true)
1534
+ * assert.strictEqual(isNonPositive(1), false)
1045
1535
  * ```
1046
1536
  *
1047
1537
  * @category combinators
@@ -1050,18 +1540,31 @@ export const struct: {
1050
1540
  export const not = <A>(self: Predicate<A>): Predicate<A> => (a) => !self(a)
1051
1541
 
1052
1542
  /**
1053
- * Combines two predicates into a new predicate that returns `true` if at least one of the predicates returns `true`.
1543
+ * Combines two predicates with a logical "OR". The resulting predicate returns `true`
1544
+ * if at least one of the predicates returns `true`.
1545
+ *
1546
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1547
+ * union of their target types (`B | C`).
1054
1548
  *
1055
1549
  * @example
1056
1550
  * ```ts
1057
1551
  * import * as assert from "node:assert"
1058
- * import { Predicate, Number } from "effect"
1552
+ * import { Predicate } from "effect"
1553
+ *
1554
+ * const isString = (u: unknown): u is string => typeof u === "string"
1555
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1556
+ *
1557
+ * const isStringOrNumber = Predicate.or(isString, isNumber)
1059
1558
  *
1060
- * const nonZero = Predicate.or(Number.lessThan(0), Number.greaterThan(0))
1559
+ * assert.strictEqual(isStringOrNumber("hello"), true)
1560
+ * assert.strictEqual(isStringOrNumber(123), true)
1561
+ * assert.strictEqual(isStringOrNumber(null), false)
1061
1562
  *
1062
- * assert.deepStrictEqual(nonZero(-1), true)
1063
- * assert.deepStrictEqual(nonZero(0), false)
1064
- * assert.deepStrictEqual(nonZero(1), true)
1563
+ * const value: unknown = "world"
1564
+ * if (isStringOrNumber(value)) {
1565
+ * // value is narrowed to string | number
1566
+ * console.log(value)
1567
+ * }
1065
1568
  * ```
1066
1569
  *
1067
1570
  * @category combinators
@@ -1069,18 +1572,31 @@ export const not = <A>(self: Predicate<A>): Predicate<A> => (a) => !self(a)
1069
1572
  */
1070
1573
  export const or: {
1071
1574
  /**
1072
- * Combines two predicates into a new predicate that returns `true` if at least one of the predicates returns `true`.
1575
+ * Combines two predicates with a logical "OR". The resulting predicate returns `true`
1576
+ * if at least one of the predicates returns `true`.
1577
+ *
1578
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1579
+ * union of their target types (`B | C`).
1073
1580
  *
1074
1581
  * @example
1075
1582
  * ```ts
1076
1583
  * import * as assert from "node:assert"
1077
- * import { Predicate, Number } from "effect"
1584
+ * import { Predicate } from "effect"
1585
+ *
1586
+ * const isString = (u: unknown): u is string => typeof u === "string"
1587
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1588
+ *
1589
+ * const isStringOrNumber = Predicate.or(isString, isNumber)
1078
1590
  *
1079
- * const nonZero = Predicate.or(Number.lessThan(0), Number.greaterThan(0))
1591
+ * assert.strictEqual(isStringOrNumber("hello"), true)
1592
+ * assert.strictEqual(isStringOrNumber(123), true)
1593
+ * assert.strictEqual(isStringOrNumber(null), false)
1080
1594
  *
1081
- * assert.deepStrictEqual(nonZero(-1), true)
1082
- * assert.deepStrictEqual(nonZero(0), false)
1083
- * assert.deepStrictEqual(nonZero(1), true)
1595
+ * const value: unknown = "world"
1596
+ * if (isStringOrNumber(value)) {
1597
+ * // value is narrowed to string | number
1598
+ * console.log(value)
1599
+ * }
1084
1600
  * ```
1085
1601
  *
1086
1602
  * @category combinators
@@ -1088,18 +1604,31 @@ export const or: {
1088
1604
  */
1089
1605
  <A, C extends A>(that: Refinement<A, C>): <B extends A>(self: Refinement<A, B>) => Refinement<A, B | C>
1090
1606
  /**
1091
- * Combines two predicates into a new predicate that returns `true` if at least one of the predicates returns `true`.
1607
+ * Combines two predicates with a logical "OR". The resulting predicate returns `true`
1608
+ * if at least one of the predicates returns `true`.
1609
+ *
1610
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1611
+ * union of their target types (`B | C`).
1092
1612
  *
1093
1613
  * @example
1094
1614
  * ```ts
1095
1615
  * import * as assert from "node:assert"
1096
- * import { Predicate, Number } from "effect"
1616
+ * import { Predicate } from "effect"
1097
1617
  *
1098
- * const nonZero = Predicate.or(Number.lessThan(0), Number.greaterThan(0))
1618
+ * const isString = (u: unknown): u is string => typeof u === "string"
1619
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1099
1620
  *
1100
- * assert.deepStrictEqual(nonZero(-1), true)
1101
- * assert.deepStrictEqual(nonZero(0), false)
1102
- * assert.deepStrictEqual(nonZero(1), true)
1621
+ * const isStringOrNumber = Predicate.or(isString, isNumber)
1622
+ *
1623
+ * assert.strictEqual(isStringOrNumber("hello"), true)
1624
+ * assert.strictEqual(isStringOrNumber(123), true)
1625
+ * assert.strictEqual(isStringOrNumber(null), false)
1626
+ *
1627
+ * const value: unknown = "world"
1628
+ * if (isStringOrNumber(value)) {
1629
+ * // value is narrowed to string | number
1630
+ * console.log(value)
1631
+ * }
1103
1632
  * ```
1104
1633
  *
1105
1634
  * @category combinators
@@ -1107,18 +1636,31 @@ export const or: {
1107
1636
  */
1108
1637
  <A, B extends A, C extends A>(self: Refinement<A, B>, that: Refinement<A, C>): Refinement<A, B | C>
1109
1638
  /**
1110
- * Combines two predicates into a new predicate that returns `true` if at least one of the predicates returns `true`.
1639
+ * Combines two predicates with a logical "OR". The resulting predicate returns `true`
1640
+ * if at least one of the predicates returns `true`.
1641
+ *
1642
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1643
+ * union of their target types (`B | C`).
1111
1644
  *
1112
1645
  * @example
1113
1646
  * ```ts
1114
1647
  * import * as assert from "node:assert"
1115
- * import { Predicate, Number } from "effect"
1648
+ * import { Predicate } from "effect"
1116
1649
  *
1117
- * const nonZero = Predicate.or(Number.lessThan(0), Number.greaterThan(0))
1650
+ * const isString = (u: unknown): u is string => typeof u === "string"
1651
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1118
1652
  *
1119
- * assert.deepStrictEqual(nonZero(-1), true)
1120
- * assert.deepStrictEqual(nonZero(0), false)
1121
- * assert.deepStrictEqual(nonZero(1), true)
1653
+ * const isStringOrNumber = Predicate.or(isString, isNumber)
1654
+ *
1655
+ * assert.strictEqual(isStringOrNumber("hello"), true)
1656
+ * assert.strictEqual(isStringOrNumber(123), true)
1657
+ * assert.strictEqual(isStringOrNumber(null), false)
1658
+ *
1659
+ * const value: unknown = "world"
1660
+ * if (isStringOrNumber(value)) {
1661
+ * // value is narrowed to string | number
1662
+ * console.log(value)
1663
+ * }
1122
1664
  * ```
1123
1665
  *
1124
1666
  * @category combinators
@@ -1126,18 +1668,31 @@ export const or: {
1126
1668
  */
1127
1669
  <A>(that: Predicate<A>): (self: Predicate<A>) => Predicate<A>
1128
1670
  /**
1129
- * Combines two predicates into a new predicate that returns `true` if at least one of the predicates returns `true`.
1671
+ * Combines two predicates with a logical "OR". The resulting predicate returns `true`
1672
+ * if at least one of the predicates returns `true`.
1673
+ *
1674
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1675
+ * union of their target types (`B | C`).
1130
1676
  *
1131
1677
  * @example
1132
1678
  * ```ts
1133
1679
  * import * as assert from "node:assert"
1134
- * import { Predicate, Number } from "effect"
1680
+ * import { Predicate } from "effect"
1135
1681
  *
1136
- * const nonZero = Predicate.or(Number.lessThan(0), Number.greaterThan(0))
1682
+ * const isString = (u: unknown): u is string => typeof u === "string"
1683
+ * const isNumber = (u: unknown): u is number => typeof u === "number"
1137
1684
  *
1138
- * assert.deepStrictEqual(nonZero(-1), true)
1139
- * assert.deepStrictEqual(nonZero(0), false)
1140
- * assert.deepStrictEqual(nonZero(1), true)
1685
+ * const isStringOrNumber = Predicate.or(isString, isNumber)
1686
+ *
1687
+ * assert.strictEqual(isStringOrNumber("hello"), true)
1688
+ * assert.strictEqual(isStringOrNumber(123), true)
1689
+ * assert.strictEqual(isStringOrNumber(null), false)
1690
+ *
1691
+ * const value: unknown = "world"
1692
+ * if (isStringOrNumber(value)) {
1693
+ * // value is narrowed to string | number
1694
+ * console.log(value)
1695
+ * }
1141
1696
  * ```
1142
1697
  *
1143
1698
  * @category combinators
@@ -1147,21 +1702,34 @@ export const or: {
1147
1702
  } = dual(2, <A>(self: Predicate<A>, that: Predicate<A>): Predicate<A> => (a) => self(a) || that(a))
1148
1703
 
1149
1704
  /**
1150
- * Combines two predicates into a new predicate that returns `true` if both of the predicates returns `true`.
1705
+ * Combines two predicates with a logical "AND". The resulting predicate returns `true`
1706
+ * only if both of the predicates return `true`.
1707
+ *
1708
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1709
+ * intersection of their target types (`B & C`).
1151
1710
  *
1152
1711
  * @example
1153
1712
  * ```ts
1154
1713
  * import * as assert from "node:assert"
1155
1714
  * import { Predicate } from "effect"
1156
1715
  *
1157
- * const minLength = (n: number) => (s: string) => s.length >= n
1158
- * const maxLength = (n: number) => (s: string) => s.length <= n
1716
+ * type Person = { name: string }
1717
+ * type Employee = { id: number }
1718
+ *
1719
+ * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string"
1720
+ * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number"
1721
+ *
1722
+ * const isPersonAndEmployee = Predicate.and(hasName, hasId)
1159
1723
  *
1160
- * const length = (n: number) => Predicate.and(minLength(n), maxLength(n))
1724
+ * const val: unknown = { name: "Alice", id: 123 }
1725
+ * if (isPersonAndEmployee(val)) {
1726
+ * // val is narrowed to Person & Employee
1727
+ * assert.strictEqual(val.name, "Alice")
1728
+ * assert.strictEqual(val.id, 123)
1729
+ * }
1161
1730
  *
1162
- * assert.deepStrictEqual(length(2)("aa"), true)
1163
- * assert.deepStrictEqual(length(2)("a"), false)
1164
- * assert.deepStrictEqual(length(2)("aaa"), false)
1731
+ * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id
1732
+ * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name
1165
1733
  * ```
1166
1734
  *
1167
1735
  * @category combinators
@@ -1169,21 +1737,34 @@ export const or: {
1169
1737
  */
1170
1738
  export const and: {
1171
1739
  /**
1172
- * Combines two predicates into a new predicate that returns `true` if both of the predicates returns `true`.
1740
+ * Combines two predicates with a logical "AND". The resulting predicate returns `true`
1741
+ * only if both of the predicates return `true`.
1742
+ *
1743
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1744
+ * intersection of their target types (`B & C`).
1173
1745
  *
1174
1746
  * @example
1175
1747
  * ```ts
1176
1748
  * import * as assert from "node:assert"
1177
1749
  * import { Predicate } from "effect"
1178
1750
  *
1179
- * const minLength = (n: number) => (s: string) => s.length >= n
1180
- * const maxLength = (n: number) => (s: string) => s.length <= n
1751
+ * type Person = { name: string }
1752
+ * type Employee = { id: number }
1753
+ *
1754
+ * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string"
1755
+ * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number"
1181
1756
  *
1182
- * const length = (n: number) => Predicate.and(minLength(n), maxLength(n))
1757
+ * const isPersonAndEmployee = Predicate.and(hasName, hasId)
1183
1758
  *
1184
- * assert.deepStrictEqual(length(2)("aa"), true)
1185
- * assert.deepStrictEqual(length(2)("a"), false)
1186
- * assert.deepStrictEqual(length(2)("aaa"), false)
1759
+ * const val: unknown = { name: "Alice", id: 123 }
1760
+ * if (isPersonAndEmployee(val)) {
1761
+ * // val is narrowed to Person & Employee
1762
+ * assert.strictEqual(val.name, "Alice")
1763
+ * assert.strictEqual(val.id, 123)
1764
+ * }
1765
+ *
1766
+ * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id
1767
+ * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name
1187
1768
  * ```
1188
1769
  *
1189
1770
  * @category combinators
@@ -1191,21 +1772,34 @@ export const and: {
1191
1772
  */
1192
1773
  <A, C extends A>(that: Refinement<A, C>): <B extends A>(self: Refinement<A, B>) => Refinement<A, B & C>
1193
1774
  /**
1194
- * Combines two predicates into a new predicate that returns `true` if both of the predicates returns `true`.
1775
+ * Combines two predicates with a logical "AND". The resulting predicate returns `true`
1776
+ * only if both of the predicates return `true`.
1777
+ *
1778
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1779
+ * intersection of their target types (`B & C`).
1195
1780
  *
1196
1781
  * @example
1197
1782
  * ```ts
1198
1783
  * import * as assert from "node:assert"
1199
1784
  * import { Predicate } from "effect"
1200
1785
  *
1201
- * const minLength = (n: number) => (s: string) => s.length >= n
1202
- * const maxLength = (n: number) => (s: string) => s.length <= n
1786
+ * type Person = { name: string }
1787
+ * type Employee = { id: number }
1203
1788
  *
1204
- * const length = (n: number) => Predicate.and(minLength(n), maxLength(n))
1789
+ * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string"
1790
+ * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number"
1791
+ *
1792
+ * const isPersonAndEmployee = Predicate.and(hasName, hasId)
1793
+ *
1794
+ * const val: unknown = { name: "Alice", id: 123 }
1795
+ * if (isPersonAndEmployee(val)) {
1796
+ * // val is narrowed to Person & Employee
1797
+ * assert.strictEqual(val.name, "Alice")
1798
+ * assert.strictEqual(val.id, 123)
1799
+ * }
1205
1800
  *
1206
- * assert.deepStrictEqual(length(2)("aa"), true)
1207
- * assert.deepStrictEqual(length(2)("a"), false)
1208
- * assert.deepStrictEqual(length(2)("aaa"), false)
1801
+ * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id
1802
+ * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name
1209
1803
  * ```
1210
1804
  *
1211
1805
  * @category combinators
@@ -1213,21 +1807,34 @@ export const and: {
1213
1807
  */
1214
1808
  <A, B extends A, C extends A>(self: Refinement<A, B>, that: Refinement<A, C>): Refinement<A, B & C>
1215
1809
  /**
1216
- * Combines two predicates into a new predicate that returns `true` if both of the predicates returns `true`.
1810
+ * Combines two predicates with a logical "AND". The resulting predicate returns `true`
1811
+ * only if both of the predicates return `true`.
1812
+ *
1813
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1814
+ * intersection of their target types (`B & C`).
1217
1815
  *
1218
1816
  * @example
1219
1817
  * ```ts
1220
1818
  * import * as assert from "node:assert"
1221
1819
  * import { Predicate } from "effect"
1222
1820
  *
1223
- * const minLength = (n: number) => (s: string) => s.length >= n
1224
- * const maxLength = (n: number) => (s: string) => s.length <= n
1821
+ * type Person = { name: string }
1822
+ * type Employee = { id: number }
1823
+ *
1824
+ * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string"
1825
+ * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number"
1826
+ *
1827
+ * const isPersonAndEmployee = Predicate.and(hasName, hasId)
1225
1828
  *
1226
- * const length = (n: number) => Predicate.and(minLength(n), maxLength(n))
1829
+ * const val: unknown = { name: "Alice", id: 123 }
1830
+ * if (isPersonAndEmployee(val)) {
1831
+ * // val is narrowed to Person & Employee
1832
+ * assert.strictEqual(val.name, "Alice")
1833
+ * assert.strictEqual(val.id, 123)
1834
+ * }
1227
1835
  *
1228
- * assert.deepStrictEqual(length(2)("aa"), true)
1229
- * assert.deepStrictEqual(length(2)("a"), false)
1230
- * assert.deepStrictEqual(length(2)("aaa"), false)
1836
+ * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id
1837
+ * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name
1231
1838
  * ```
1232
1839
  *
1233
1840
  * @category combinators
@@ -1235,21 +1842,34 @@ export const and: {
1235
1842
  */
1236
1843
  <A>(that: Predicate<A>): (self: Predicate<A>) => Predicate<A>
1237
1844
  /**
1238
- * Combines two predicates into a new predicate that returns `true` if both of the predicates returns `true`.
1845
+ * Combines two predicates with a logical "AND". The resulting predicate returns `true`
1846
+ * only if both of the predicates return `true`.
1847
+ *
1848
+ * If both predicates are `Refinement`s, the resulting predicate is a `Refinement` to the
1849
+ * intersection of their target types (`B & C`).
1239
1850
  *
1240
1851
  * @example
1241
1852
  * ```ts
1242
1853
  * import * as assert from "node:assert"
1243
1854
  * import { Predicate } from "effect"
1244
1855
  *
1245
- * const minLength = (n: number) => (s: string) => s.length >= n
1246
- * const maxLength = (n: number) => (s: string) => s.length <= n
1856
+ * type Person = { name: string }
1857
+ * type Employee = { id: number }
1858
+ *
1859
+ * const hasName = (u: unknown): u is Person => Predicate.hasProperty(u, "name") && typeof (u as any).name === "string"
1860
+ * const hasId = (u: unknown): u is Employee => Predicate.hasProperty(u, "id") && typeof (u as any).id === "number"
1247
1861
  *
1248
- * const length = (n: number) => Predicate.and(minLength(n), maxLength(n))
1862
+ * const isPersonAndEmployee = Predicate.and(hasName, hasId)
1249
1863
  *
1250
- * assert.deepStrictEqual(length(2)("aa"), true)
1251
- * assert.deepStrictEqual(length(2)("a"), false)
1252
- * assert.deepStrictEqual(length(2)("aaa"), false)
1864
+ * const val: unknown = { name: "Alice", id: 123 }
1865
+ * if (isPersonAndEmployee(val)) {
1866
+ * // val is narrowed to Person & Employee
1867
+ * assert.strictEqual(val.name, "Alice")
1868
+ * assert.strictEqual(val.id, 123)
1869
+ * }
1870
+ *
1871
+ * assert.strictEqual(isPersonAndEmployee({ name: "Bob" }), false) // Missing id
1872
+ * assert.strictEqual(isPersonAndEmployee({ id: 456 }), false) // Missing name
1253
1873
  * ```
1254
1874
  *
1255
1875
  * @category combinators
@@ -1259,16 +1879,73 @@ export const and: {
1259
1879
  } = dual(2, <A>(self: Predicate<A>, that: Predicate<A>): Predicate<A> => (a) => self(a) && that(a))
1260
1880
 
1261
1881
  /**
1882
+ * Combines two predicates with a logical "XOR" (exclusive OR). The resulting predicate
1883
+ * returns `true` if one of the predicates returns `true`, but not both.
1884
+ *
1885
+ * @example
1886
+ * ```ts
1887
+ * import * as assert from "node:assert"
1888
+ * import { Predicate } from "effect"
1889
+ *
1890
+ * const isPositive = (n: number) => n > 0
1891
+ * const isEven = (n: number) => n % 2 === 0
1892
+ *
1893
+ * const isPositiveXorEven = Predicate.xor(isPositive, isEven)
1894
+ *
1895
+ * assert.strictEqual(isPositiveXorEven(4), false) // both true -> false
1896
+ * assert.strictEqual(isPositiveXorEven(3), true) // one true -> true
1897
+ * assert.strictEqual(isPositiveXorEven(-2), true) // one true -> true
1898
+ * assert.strictEqual(isPositiveXorEven(-1), false) // both false -> false
1899
+ * ```
1900
+ *
1262
1901
  * @category combinators
1263
1902
  * @since 2.0.0
1264
1903
  */
1265
1904
  export const xor: {
1266
1905
  /**
1906
+ * Combines two predicates with a logical "XOR" (exclusive OR). The resulting predicate
1907
+ * returns `true` if one of the predicates returns `true`, but not both.
1908
+ *
1909
+ * @example
1910
+ * ```ts
1911
+ * import * as assert from "node:assert"
1912
+ * import { Predicate } from "effect"
1913
+ *
1914
+ * const isPositive = (n: number) => n > 0
1915
+ * const isEven = (n: number) => n % 2 === 0
1916
+ *
1917
+ * const isPositiveXorEven = Predicate.xor(isPositive, isEven)
1918
+ *
1919
+ * assert.strictEqual(isPositiveXorEven(4), false) // both true -> false
1920
+ * assert.strictEqual(isPositiveXorEven(3), true) // one true -> true
1921
+ * assert.strictEqual(isPositiveXorEven(-2), true) // one true -> true
1922
+ * assert.strictEqual(isPositiveXorEven(-1), false) // both false -> false
1923
+ * ```
1924
+ *
1267
1925
  * @category combinators
1268
1926
  * @since 2.0.0
1269
1927
  */
1270
1928
  <A>(that: Predicate<A>): (self: Predicate<A>) => Predicate<A>
1271
1929
  /**
1930
+ * Combines two predicates with a logical "XOR" (exclusive OR). The resulting predicate
1931
+ * returns `true` if one of the predicates returns `true`, but not both.
1932
+ *
1933
+ * @example
1934
+ * ```ts
1935
+ * import * as assert from "node:assert"
1936
+ * import { Predicate } from "effect"
1937
+ *
1938
+ * const isPositive = (n: number) => n > 0
1939
+ * const isEven = (n: number) => n % 2 === 0
1940
+ *
1941
+ * const isPositiveXorEven = Predicate.xor(isPositive, isEven)
1942
+ *
1943
+ * assert.strictEqual(isPositiveXorEven(4), false) // both true -> false
1944
+ * assert.strictEqual(isPositiveXorEven(3), true) // one true -> true
1945
+ * assert.strictEqual(isPositiveXorEven(-2), true) // one true -> true
1946
+ * assert.strictEqual(isPositiveXorEven(-1), false) // both false -> false
1947
+ * ```
1948
+ *
1272
1949
  * @category combinators
1273
1950
  * @since 2.0.0
1274
1951
  */
@@ -1276,16 +1953,73 @@ export const xor: {
1276
1953
  } = dual(2, <A>(self: Predicate<A>, that: Predicate<A>): Predicate<A> => (a) => self(a) !== that(a))
1277
1954
 
1278
1955
  /**
1956
+ * Combines two predicates with a logical "EQV" (equivalence). The resulting predicate
1957
+ * returns `true` if both predicates return the same boolean value (both `true` or both `false`).
1958
+ *
1959
+ * @example
1960
+ * ```ts
1961
+ * import * as assert from "node:assert"
1962
+ * import { Predicate } from "effect"
1963
+ *
1964
+ * const isPositive = (n: number) => n > 0
1965
+ * const isEven = (n: number) => n % 2 === 0
1966
+ *
1967
+ * const isPositiveEqvEven = Predicate.eqv(isPositive, isEven)
1968
+ *
1969
+ * assert.strictEqual(isPositiveEqvEven(4), true) // both true -> true
1970
+ * assert.strictEqual(isPositiveEqvEven(3), false) // different -> false
1971
+ * assert.strictEqual(isPositiveEqvEven(-2), false) // different -> false
1972
+ * assert.strictEqual(isPositiveEqvEven(-1), true) // both false -> true
1973
+ * ```
1974
+ *
1279
1975
  * @category combinators
1280
1976
  * @since 2.0.0
1281
1977
  */
1282
1978
  export const eqv: {
1283
1979
  /**
1980
+ * Combines two predicates with a logical "EQV" (equivalence). The resulting predicate
1981
+ * returns `true` if both predicates return the same boolean value (both `true` or both `false`).
1982
+ *
1983
+ * @example
1984
+ * ```ts
1985
+ * import * as assert from "node:assert"
1986
+ * import { Predicate } from "effect"
1987
+ *
1988
+ * const isPositive = (n: number) => n > 0
1989
+ * const isEven = (n: number) => n % 2 === 0
1990
+ *
1991
+ * const isPositiveEqvEven = Predicate.eqv(isPositive, isEven)
1992
+ *
1993
+ * assert.strictEqual(isPositiveEqvEven(4), true) // both true -> true
1994
+ * assert.strictEqual(isPositiveEqvEven(3), false) // different -> false
1995
+ * assert.strictEqual(isPositiveEqvEven(-2), false) // different -> false
1996
+ * assert.strictEqual(isPositiveEqvEven(-1), true) // both false -> true
1997
+ * ```
1998
+ *
1284
1999
  * @category combinators
1285
2000
  * @since 2.0.0
1286
2001
  */
1287
2002
  <A>(that: Predicate<A>): (self: Predicate<A>) => Predicate<A>
1288
2003
  /**
2004
+ * Combines two predicates with a logical "EQV" (equivalence). The resulting predicate
2005
+ * returns `true` if both predicates return the same boolean value (both `true` or both `false`).
2006
+ *
2007
+ * @example
2008
+ * ```ts
2009
+ * import * as assert from "node:assert"
2010
+ * import { Predicate } from "effect"
2011
+ *
2012
+ * const isPositive = (n: number) => n > 0
2013
+ * const isEven = (n: number) => n % 2 === 0
2014
+ *
2015
+ * const isPositiveEqvEven = Predicate.eqv(isPositive, isEven)
2016
+ *
2017
+ * assert.strictEqual(isPositiveEqvEven(4), true) // both true -> true
2018
+ * assert.strictEqual(isPositiveEqvEven(3), false) // different -> false
2019
+ * assert.strictEqual(isPositiveEqvEven(-2), false) // different -> false
2020
+ * assert.strictEqual(isPositiveEqvEven(-1), true) // both false -> true
2021
+ * ```
2022
+ *
1289
2023
  * @category combinators
1290
2024
  * @since 2.0.0
1291
2025
  */
@@ -1293,47 +2027,59 @@ export const eqv: {
1293
2027
  } = dual(2, <A>(self: Predicate<A>, that: Predicate<A>): Predicate<A> => (a) => self(a) === that(a))
1294
2028
 
1295
2029
  /**
1296
- * Represents the logical implication combinator for predicates. In formal
1297
- * logic, the implication operator `->` denotes that if the first proposition
1298
- * (antecedent) is true, then the second proposition (consequent) must also be
1299
- * true. In simpler terms, `p implies q` can be interpreted as "if p then q". If
1300
- * the first predicate holds, then the second predicate must hold
1301
- * for the given context.
2030
+ * Creates a predicate that represents a logical "if-then" rule.
1302
2031
  *
1303
- * In practical terms within TypeScript, `p implies q` is equivalent to `!p || (p && q)`.
2032
+ * Think of it as a conditional promise: **"If `antecedent` holds true, then I promise `consequent` will also be true."**
1304
2033
  *
1305
- * Note that if the antecedent is `false`, the result is `true` by default
1306
- * because the outcome of the consequent cannot be determined.
2034
+ * This function is invaluable for defining complex validation logic where one condition dictates another.
1307
2035
  *
1308
- * This function is useful in situations where you need to enforce rules or
1309
- * constraints that are contingent on certain conditions.
1310
- * It proves especially helpful in defining property tests.
2036
+ * ### How It Works
1311
2037
  *
1312
- * The example below illustrates the transitive property of order using the
1313
- * `implies` function. In simple terms, if `a <= b` and `b <= c`, then `a <= c`
1314
- * must be true.
2038
+ * The rule only fails (returns `false`) when the "if" part is `true`, but the "then" part is `false`.
2039
+ * In all other cases, the promise is considered kept, and the result is `true`.
2040
+ *
2041
+ * This includes the concept of **"vacuous truth"**: if the "if" part is `false`, the rule doesn't apply,
2042
+ * so the promise isn't broken, and the result is `true`. (e.g., "If it rains, I'll bring an umbrella."
2043
+ * If it doesn't rain, you haven't broken your promise, no matter what).
2044
+ *
2045
+ * ### Key Details
2046
+ *
2047
+ * - **Logical Equivalence**: `implies(p, q)` is the same as `not(p).or(q)`, or simply `!p || q`
2048
+ * in plain JavaScript. This can be a helpful way to reason about its behavior.
2049
+ *
2050
+ * - **Type-Safety Warning**: This function always returns a `Predicate`, never a type-narrowing
2051
+ * `Refinement`. A `true` result doesn't guarantee the `consequent` passed (it could be `true`
2052
+ * simply because the `antecedent` was `false`), so it cannot be used to safely narrow a type.
1315
2053
  *
1316
2054
  * @example
1317
2055
  * ```ts
2056
+ * // Rule: A user can only be an admin if they also belong to the "staff" group.
1318
2057
  * import * as assert from "node:assert"
1319
2058
  * import { Predicate } from "effect"
1320
2059
  *
1321
- * type Triple = {
1322
- * readonly a: number
1323
- * readonly b: number
1324
- * readonly c: number
2060
+ * type User = {
2061
+ * isStaff: boolean
2062
+ * isAdmin: boolean
1325
2063
  * }
1326
2064
  *
1327
- * const transitivity = Predicate.implies(
1328
- * // antecedent
1329
- * (input: Triple) => input.a <= input.b && input.b <= input.c,
1330
- * // consequent
1331
- * (input: Triple) => input.a <= input.c
2065
+ * const isValidUserPermission = Predicate.implies(
2066
+ * // antecedent: "if" the user is an admin...
2067
+ * (user: User) => user.isAdmin,
2068
+ * // consequent: "then" they must be staff.
2069
+ * (user: User) => user.isStaff
1332
2070
  * )
1333
2071
  *
1334
- * assert.equal(transitivity({ a: 1, b: 2, c: 3 }), true)
1335
- * // antecedent is `false`, so the result is `true`
1336
- * assert.equal(transitivity({ a: 1, b: 0, c: 0 }), true)
2072
+ * // A non-admin who is not staff. Rule doesn't apply (antecedent is false).
2073
+ * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: false }), true)
2074
+ *
2075
+ * // A staff member who is not an admin. Rule doesn't apply (antecedent is false).
2076
+ * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: false }), true)
2077
+ *
2078
+ * // An admin who is also staff. The rule was followed.
2079
+ * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: true }), true)
2080
+ *
2081
+ * // An admin who is NOT staff. The rule was broken!
2082
+ * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: true }), false)
1337
2083
  * ```
1338
2084
  *
1339
2085
  * @category combinators
@@ -1341,47 +2087,59 @@ export const eqv: {
1341
2087
  */
1342
2088
  export const implies: {
1343
2089
  /**
1344
- * Represents the logical implication combinator for predicates. In formal
1345
- * logic, the implication operator `->` denotes that if the first proposition
1346
- * (antecedent) is true, then the second proposition (consequent) must also be
1347
- * true. In simpler terms, `p implies q` can be interpreted as "if p then q". If
1348
- * the first predicate holds, then the second predicate must hold
1349
- * for the given context.
2090
+ * Creates a predicate that represents a logical "if-then" rule.
1350
2091
  *
1351
- * In practical terms within TypeScript, `p implies q` is equivalent to `!p || (p && q)`.
2092
+ * Think of it as a conditional promise: **"If `antecedent` holds true, then I promise `consequent` will also be true."**
1352
2093
  *
1353
- * Note that if the antecedent is `false`, the result is `true` by default
1354
- * because the outcome of the consequent cannot be determined.
2094
+ * This function is invaluable for defining complex validation logic where one condition dictates another.
1355
2095
  *
1356
- * This function is useful in situations where you need to enforce rules or
1357
- * constraints that are contingent on certain conditions.
1358
- * It proves especially helpful in defining property tests.
2096
+ * ### How It Works
1359
2097
  *
1360
- * The example below illustrates the transitive property of order using the
1361
- * `implies` function. In simple terms, if `a <= b` and `b <= c`, then `a <= c`
1362
- * must be true.
2098
+ * The rule only fails (returns `false`) when the "if" part is `true`, but the "then" part is `false`.
2099
+ * In all other cases, the promise is considered kept, and the result is `true`.
2100
+ *
2101
+ * This includes the concept of **"vacuous truth"**: if the "if" part is `false`, the rule doesn't apply,
2102
+ * so the promise isn't broken, and the result is `true`. (e.g., "If it rains, I'll bring an umbrella."
2103
+ * If it doesn't rain, you haven't broken your promise, no matter what).
2104
+ *
2105
+ * ### Key Details
2106
+ *
2107
+ * - **Logical Equivalence**: `implies(p, q)` is the same as `not(p).or(q)`, or simply `!p || q`
2108
+ * in plain JavaScript. This can be a helpful way to reason about its behavior.
2109
+ *
2110
+ * - **Type-Safety Warning**: This function always returns a `Predicate`, never a type-narrowing
2111
+ * `Refinement`. A `true` result doesn't guarantee the `consequent` passed (it could be `true`
2112
+ * simply because the `antecedent` was `false`), so it cannot be used to safely narrow a type.
1363
2113
  *
1364
2114
  * @example
1365
2115
  * ```ts
2116
+ * // Rule: A user can only be an admin if they also belong to the "staff" group.
1366
2117
  * import * as assert from "node:assert"
1367
2118
  * import { Predicate } from "effect"
1368
2119
  *
1369
- * type Triple = {
1370
- * readonly a: number
1371
- * readonly b: number
1372
- * readonly c: number
2120
+ * type User = {
2121
+ * isStaff: boolean
2122
+ * isAdmin: boolean
1373
2123
  * }
1374
2124
  *
1375
- * const transitivity = Predicate.implies(
1376
- * // antecedent
1377
- * (input: Triple) => input.a <= input.b && input.b <= input.c,
1378
- * // consequent
1379
- * (input: Triple) => input.a <= input.c
2125
+ * const isValidUserPermission = Predicate.implies(
2126
+ * // antecedent: "if" the user is an admin...
2127
+ * (user: User) => user.isAdmin,
2128
+ * // consequent: "then" they must be staff.
2129
+ * (user: User) => user.isStaff
1380
2130
  * )
1381
2131
  *
1382
- * assert.equal(transitivity({ a: 1, b: 2, c: 3 }), true)
1383
- * // antecedent is `false`, so the result is `true`
1384
- * assert.equal(transitivity({ a: 1, b: 0, c: 0 }), true)
2132
+ * // A non-admin who is not staff. Rule doesn't apply (antecedent is false).
2133
+ * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: false }), true)
2134
+ *
2135
+ * // A staff member who is not an admin. Rule doesn't apply (antecedent is false).
2136
+ * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: false }), true)
2137
+ *
2138
+ * // An admin who is also staff. The rule was followed.
2139
+ * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: true }), true)
2140
+ *
2141
+ * // An admin who is NOT staff. The rule was broken!
2142
+ * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: true }), false)
1385
2143
  * ```
1386
2144
  *
1387
2145
  * @category combinators
@@ -1389,47 +2147,59 @@ export const implies: {
1389
2147
  */
1390
2148
  <A>(consequent: Predicate<A>): (antecedent: Predicate<A>) => Predicate<A>
1391
2149
  /**
1392
- * Represents the logical implication combinator for predicates. In formal
1393
- * logic, the implication operator `->` denotes that if the first proposition
1394
- * (antecedent) is true, then the second proposition (consequent) must also be
1395
- * true. In simpler terms, `p implies q` can be interpreted as "if p then q". If
1396
- * the first predicate holds, then the second predicate must hold
1397
- * for the given context.
2150
+ * Creates a predicate that represents a logical "if-then" rule.
2151
+ *
2152
+ * Think of it as a conditional promise: **"If `antecedent` holds true, then I promise `consequent` will also be true."**
2153
+ *
2154
+ * This function is invaluable for defining complex validation logic where one condition dictates another.
1398
2155
  *
1399
- * In practical terms within TypeScript, `p implies q` is equivalent to `!p || (p && q)`.
2156
+ * ### How It Works
1400
2157
  *
1401
- * Note that if the antecedent is `false`, the result is `true` by default
1402
- * because the outcome of the consequent cannot be determined.
2158
+ * The rule only fails (returns `false`) when the "if" part is `true`, but the "then" part is `false`.
2159
+ * In all other cases, the promise is considered kept, and the result is `true`.
1403
2160
  *
1404
- * This function is useful in situations where you need to enforce rules or
1405
- * constraints that are contingent on certain conditions.
1406
- * It proves especially helpful in defining property tests.
2161
+ * This includes the concept of **"vacuous truth"**: if the "if" part is `false`, the rule doesn't apply,
2162
+ * so the promise isn't broken, and the result is `true`. (e.g., "If it rains, I'll bring an umbrella."
2163
+ * If it doesn't rain, you haven't broken your promise, no matter what).
1407
2164
  *
1408
- * The example below illustrates the transitive property of order using the
1409
- * `implies` function. In simple terms, if `a <= b` and `b <= c`, then `a <= c`
1410
- * must be true.
2165
+ * ### Key Details
2166
+ *
2167
+ * - **Logical Equivalence**: `implies(p, q)` is the same as `not(p).or(q)`, or simply `!p || q`
2168
+ * in plain JavaScript. This can be a helpful way to reason about its behavior.
2169
+ *
2170
+ * - **Type-Safety Warning**: This function always returns a `Predicate`, never a type-narrowing
2171
+ * `Refinement`. A `true` result doesn't guarantee the `consequent` passed (it could be `true`
2172
+ * simply because the `antecedent` was `false`), so it cannot be used to safely narrow a type.
1411
2173
  *
1412
2174
  * @example
1413
2175
  * ```ts
2176
+ * // Rule: A user can only be an admin if they also belong to the "staff" group.
1414
2177
  * import * as assert from "node:assert"
1415
2178
  * import { Predicate } from "effect"
1416
2179
  *
1417
- * type Triple = {
1418
- * readonly a: number
1419
- * readonly b: number
1420
- * readonly c: number
2180
+ * type User = {
2181
+ * isStaff: boolean
2182
+ * isAdmin: boolean
1421
2183
  * }
1422
2184
  *
1423
- * const transitivity = Predicate.implies(
1424
- * // antecedent
1425
- * (input: Triple) => input.a <= input.b && input.b <= input.c,
1426
- * // consequent
1427
- * (input: Triple) => input.a <= input.c
2185
+ * const isValidUserPermission = Predicate.implies(
2186
+ * // antecedent: "if" the user is an admin...
2187
+ * (user: User) => user.isAdmin,
2188
+ * // consequent: "then" they must be staff.
2189
+ * (user: User) => user.isStaff
1428
2190
  * )
1429
2191
  *
1430
- * assert.equal(transitivity({ a: 1, b: 2, c: 3 }), true)
1431
- * // antecedent is `false`, so the result is `true`
1432
- * assert.equal(transitivity({ a: 1, b: 0, c: 0 }), true)
2192
+ * // A non-admin who is not staff. Rule doesn't apply (antecedent is false).
2193
+ * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: false }), true)
2194
+ *
2195
+ * // A staff member who is not an admin. Rule doesn't apply (antecedent is false).
2196
+ * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: false }), true)
2197
+ *
2198
+ * // An admin who is also staff. The rule was followed.
2199
+ * assert.strictEqual(isValidUserPermission({ isStaff: true, isAdmin: true }), true)
2200
+ *
2201
+ * // An admin who is NOT staff. The rule was broken!
2202
+ * assert.strictEqual(isValidUserPermission({ isStaff: false, isAdmin: true }), false)
1433
2203
  * ```
1434
2204
  *
1435
2205
  * @category combinators
@@ -1442,16 +2212,28 @@ export const implies: {
1442
2212
  )
1443
2213
 
1444
2214
  /**
2215
+ * Combines two predicates with a logical "NOR" (negated OR). The resulting predicate
2216
+ * returns `true` only if both predicates return `false`.
2217
+ * This is equivalent to `not(or(p, q))`.
2218
+ *
1445
2219
  * @category combinators
1446
2220
  * @since 2.0.0
1447
2221
  */
1448
2222
  export const nor: {
1449
2223
  /**
2224
+ * Combines two predicates with a logical "NOR" (negated OR). The resulting predicate
2225
+ * returns `true` only if both predicates return `false`.
2226
+ * This is equivalent to `not(or(p, q))`.
2227
+ *
1450
2228
  * @category combinators
1451
2229
  * @since 2.0.0
1452
2230
  */
1453
2231
  <A>(that: Predicate<A>): (self: Predicate<A>) => Predicate<A>
1454
2232
  /**
2233
+ * Combines two predicates with a logical "NOR" (negated OR). The resulting predicate
2234
+ * returns `true` only if both predicates return `false`.
2235
+ * This is equivalent to `not(or(p, q))`.
2236
+ *
1455
2237
  * @category combinators
1456
2238
  * @since 2.0.0
1457
2239
  */
@@ -1462,16 +2244,28 @@ export const nor: {
1462
2244
  )
1463
2245
 
1464
2246
  /**
2247
+ * Combines two predicates with a logical "NAND" (negated AND). The resulting predicate
2248
+ * returns `true` if at least one of the predicates returns `false`.
2249
+ * This is equivalent to `not(and(p, q))`.
2250
+ *
1465
2251
  * @category combinators
1466
2252
  * @since 2.0.0
1467
2253
  */
1468
2254
  export const nand: {
1469
2255
  /**
2256
+ * Combines two predicates with a logical "NAND" (negated AND). The resulting predicate
2257
+ * returns `true` if at least one of the predicates returns `false`.
2258
+ * This is equivalent to `not(and(p, q))`.
2259
+ *
1470
2260
  * @category combinators
1471
2261
  * @since 2.0.0
1472
2262
  */
1473
2263
  <A>(that: Predicate<A>): (self: Predicate<A>) => Predicate<A>
1474
2264
  /**
2265
+ * Combines two predicates with a logical "NAND" (negated AND). The resulting predicate
2266
+ * returns `true` if at least one of the predicates returns `false`.
2267
+ * This is equivalent to `not(and(p, q))`.
2268
+ *
1475
2269
  * @category combinators
1476
2270
  * @since 2.0.0
1477
2271
  */
@@ -1482,8 +2276,29 @@ export const nand: {
1482
2276
  )
1483
2277
 
1484
2278
  /**
2279
+ * Takes an iterable of predicates and returns a new predicate. The new predicate
2280
+ * returns `true` if all predicates in the collection return `true` for a given value.
2281
+ *
2282
+ * This is like `Array.prototype.every` but for a collection of predicates.
2283
+ *
2284
+ * @example
2285
+ * ```ts
2286
+ * import * as assert from "node:assert"
2287
+ * import { Predicate } from "effect"
2288
+ *
2289
+ * const isPositive = (n: number) => n > 0
2290
+ * const isEven = (n: number) => n % 2 === 0
2291
+ *
2292
+ * const isPositiveAndEven = Predicate.every([isPositive, isEven])
2293
+ *
2294
+ * assert.strictEqual(isPositiveAndEven(4), true)
2295
+ * assert.strictEqual(isPositiveAndEven(3), false)
2296
+ * assert.strictEqual(isPositiveAndEven(-2), false)
2297
+ * ```
2298
+ *
1485
2299
  * @category elements
1486
2300
  * @since 2.0.0
2301
+ * @see some
1487
2302
  */
1488
2303
  export const every = <A>(collection: Iterable<Predicate<A>>): Predicate<A> => (a: A) => {
1489
2304
  for (const p of collection) {
@@ -1495,8 +2310,29 @@ export const every = <A>(collection: Iterable<Predicate<A>>): Predicate<A> => (a
1495
2310
  }
1496
2311
 
1497
2312
  /**
2313
+ * Takes an iterable of predicates and returns a new predicate. The new predicate
2314
+ * returns `true` if at least one predicate in the collection returns `true` for a given value.
2315
+ *
2316
+ * This is like `Array.prototype.some` but for a collection of predicates.
2317
+ *
2318
+ * @example
2319
+ * ```ts
2320
+ * import * as assert from "node:assert"
2321
+ * import { Predicate } from "effect"
2322
+ *
2323
+ * const isNegative = (n: number) => n < 0
2324
+ * const isOdd = (n: number) => n % 2 !== 0
2325
+ *
2326
+ * const isNegativeOrOdd = Predicate.some([isNegative, isOdd])
2327
+ *
2328
+ * assert.strictEqual(isNegativeOrOdd(-2), true) // isNegative is true
2329
+ * assert.strictEqual(isNegativeOrOdd(3), true) // isOdd is true
2330
+ * assert.strictEqual(isNegativeOrOdd(4), false) // both are false
2331
+ * ```
2332
+ *
1498
2333
  * @category elements
1499
2334
  * @since 2.0.0
2335
+ * @see every
1500
2336
  */
1501
2337
  export const some = <A>(collection: Iterable<Predicate<A>>): Predicate<A> => (a) => {
1502
2338
  for (const p of collection) {