@typed/guard 1.0.0-beta.4 → 1.0.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -22
- package/dist/index.d.ts +519 -34
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +408 -19
- package/examples/basic.ts +23 -0
- package/package.json +27 -14
- package/dist/index.test.d.ts +0 -2
- package/dist/index.test.d.ts.map +0 -1
- package/dist/index.test.js +0 -314
- package/src/index.test.ts +0 -366
- package/src/index.ts +0 -656
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
+
import type * as Arr from "effect/Array";
|
|
4
5
|
import type * as Cause from "effect/Cause";
|
|
5
6
|
import * as Effect from "effect/Effect";
|
|
6
7
|
import type * as Layer from "effect/Layer";
|
|
@@ -8,7 +9,31 @@ import * as Option from "effect/Option";
|
|
|
8
9
|
import type * as Predicate from "effect/Predicate";
|
|
9
10
|
import * as Schema from "effect/Schema";
|
|
10
11
|
import type * as Context from "effect/Context";
|
|
12
|
+
import type { ExcludeTag, ExtractTag, NoInfer, Tags } from "effect/Types";
|
|
11
13
|
/**
|
|
14
|
+
* An effectful partial transformation.
|
|
15
|
+
*
|
|
16
|
+
* A successful `Some` contains a match, a successful `None` means the input did
|
|
17
|
+
* not match, and an Effect failure remains in the `E` channel. Required
|
|
18
|
+
* services remain in `R`.
|
|
19
|
+
*
|
|
20
|
+
* @remarks
|
|
21
|
+
* ## Why
|
|
22
|
+
* `Guard` models non-match as successful `None`, keeping ordinary dispatch separate from typed failure, defects, and interruption in Effect's Cause.
|
|
23
|
+
*
|
|
24
|
+
* ## Ownership and lifetime
|
|
25
|
+
* A Guard acquires no resources by itself; each invocation has the lifetime and service requirements of the returned Effect.
|
|
26
|
+
*
|
|
27
|
+
* See [Effect Option](https://effect.website/docs/data-types/option/) and [Effect error management](https://effect.website/docs/error-management/).
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* import type { Guard } from "@typed/guard"
|
|
32
|
+
* import { Effect, Option } from "effect"
|
|
33
|
+
* const string: Guard<unknown, string> = (input) => Effect.succeed(typeof input === "string" ? Option.some(input) : Option.none())
|
|
34
|
+
* ```
|
|
35
|
+
*
|
|
36
|
+
* @category Models
|
|
12
37
|
* @since 1.0.0
|
|
13
38
|
*/
|
|
14
39
|
export type Guard<in I, out O, out E = never, out R = never> = (input: I) => Effect.Effect<Option.Option<O>, E, R>;
|
|
@@ -17,37 +42,165 @@ export type Guard<in I, out O, out E = never, out R = never> = (input: I) => Eff
|
|
|
17
42
|
*/
|
|
18
43
|
export declare namespace Guard {
|
|
19
44
|
/**
|
|
45
|
+
* Extracts the accepted input type from a Guard or Guard adapter.
|
|
46
|
+
* @remarks
|
|
47
|
+
* ## Why
|
|
48
|
+
* The extractor keeps generic APIs aligned with the exact input contract without repeating inference logic.
|
|
49
|
+
* ## Ownership and lifetime
|
|
50
|
+
* This compile-time type acquires no resources and has no runtime lifetime.
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* import type { Guard } from "@typed/guard"
|
|
54
|
+
* type Input = Guard.Input<Guard<string, number>>
|
|
55
|
+
* ```
|
|
56
|
+
* @category Type helpers
|
|
20
57
|
* @since 1.0.0
|
|
21
58
|
*/
|
|
22
59
|
type Input<T> = [T] extends [Guard<infer I, infer _R, infer _E, infer _O>] ? I : [T] extends [AsGuard<infer I, infer _R, infer _E, infer _O>] ? I : never;
|
|
23
60
|
/**
|
|
61
|
+
* Extracts the Effect service requirements from a Guard or Guard adapter.
|
|
62
|
+
* @remarks
|
|
63
|
+
* ## Why
|
|
64
|
+
* Service extraction makes environment composition visible in combinator signatures.
|
|
65
|
+
* ## Ownership and lifetime
|
|
66
|
+
* This compile-time type acquires no resources; service lifetime is governed by the resulting Effect or Layer.
|
|
67
|
+
* @example
|
|
68
|
+
* ```ts
|
|
69
|
+
* import type { Guard } from "@typed/guard"
|
|
70
|
+
* type Services = Guard.Services<Guard<string, number, never, { readonly Db: unique symbol }>>
|
|
71
|
+
* ```
|
|
72
|
+
* @category Type helpers
|
|
24
73
|
* @since 1.0.0
|
|
25
74
|
*/
|
|
26
75
|
type Services<T> = [T] extends [Guard<infer _I, infer _O, infer _E, infer R>] ? R : [T] extends [AsGuard<infer _I, infer _O, infer _E, infer R>] ? R : never;
|
|
27
76
|
/**
|
|
77
|
+
* Extracts the typed error channel from a Guard or Guard adapter.
|
|
78
|
+
* @remarks
|
|
79
|
+
* ## Why
|
|
80
|
+
* Typed failure remains distinct from `None`, defects, and interruption throughout composition.
|
|
81
|
+
* ## Ownership and lifetime
|
|
82
|
+
* This compile-time type acquires no resources and has no runtime lifetime.
|
|
83
|
+
* @example
|
|
84
|
+
* ```ts
|
|
85
|
+
* import type { Guard } from "@typed/guard"
|
|
86
|
+
* type Error = Guard.Error<Guard<string, number, "Invalid">>
|
|
87
|
+
* ```
|
|
88
|
+
* @category Type helpers
|
|
28
89
|
* @since 1.0.0
|
|
29
90
|
*/
|
|
30
91
|
type Error<T> = [T] extends [Guard<infer _I, infer _O, infer E, infer _R>] ? E : [T] extends [AsGuard<infer _I, infer _O, infer E, infer _R>] ? E : never;
|
|
31
92
|
/**
|
|
93
|
+
* Extracts the matched output type from a Guard or Guard adapter.
|
|
94
|
+
* @remarks
|
|
95
|
+
* ## Why
|
|
96
|
+
* The extractor lets record and dispatch combinators derive their output without duplicating conditional types.
|
|
97
|
+
* ## Ownership and lifetime
|
|
98
|
+
* This compile-time type acquires no resources and has no runtime lifetime.
|
|
99
|
+
* @example
|
|
100
|
+
* ```ts
|
|
101
|
+
* import type { Guard } from "@typed/guard"
|
|
102
|
+
* type Output = Guard.Output<Guard<string, number>>
|
|
103
|
+
* ```
|
|
104
|
+
* @category Type helpers
|
|
32
105
|
* @since 1.0.0
|
|
33
106
|
*/
|
|
34
107
|
type Output<T> = [T] extends [Guard<infer _I, infer O, infer _E, infer _R>] ? O : [T] extends [AsGuard<infer _I, infer O, infer _E, infer _R>] ? O : never;
|
|
35
108
|
}
|
|
36
109
|
/**
|
|
110
|
+
* An object that supplies a Guard through an own callable `asGuard` property.
|
|
111
|
+
* Use an instance field rather than a prototype method.
|
|
112
|
+
*
|
|
113
|
+
* @remarks
|
|
114
|
+
* ## Why
|
|
115
|
+
* An explicit adapter protocol lets domain objects participate in Guard composition without inheritance or wrapper allocation.
|
|
116
|
+
*
|
|
117
|
+
* ## Ownership and lifetime
|
|
118
|
+
* The adapter acquires no resources; the returned Guard owns no lifetime beyond each returned Effect.
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```ts
|
|
122
|
+
* import type { AsGuard, Guard } from "@typed/guard"
|
|
123
|
+
* import { Effect } from "effect"
|
|
124
|
+
* const adapter: AsGuard<string, string> = { asGuard: () => ((input) => Effect.succeedSome(input)) as Guard<string, string> }
|
|
125
|
+
* ```
|
|
126
|
+
*
|
|
127
|
+
* @category Models
|
|
37
128
|
* @since 1.0.0
|
|
38
129
|
*/
|
|
39
130
|
export interface AsGuard<in I, out O, out E = never, out R = never> {
|
|
131
|
+
/**
|
|
132
|
+
* Returns the Guard represented by this adapter.
|
|
133
|
+
* @remarks
|
|
134
|
+
* ## Why
|
|
135
|
+
* Requiring an own callable property avoids ambiguous prototype behavior when adapters cross object boundaries.
|
|
136
|
+
* ## Ownership and lifetime
|
|
137
|
+
* Calling this property acquires no resources; the returned Guard follows its own Effect lifetime.
|
|
138
|
+
* @category Models
|
|
139
|
+
* @since 1.0.0
|
|
140
|
+
*/
|
|
40
141
|
readonly asGuard: () => Guard<I, O, E, R>;
|
|
41
142
|
}
|
|
42
143
|
/**
|
|
144
|
+
* A Guard or an object that supplies one. Guard combinators accept either form.
|
|
145
|
+
*
|
|
146
|
+
* @remarks
|
|
147
|
+
* ## Why
|
|
148
|
+
* A single input contract lets every combinator accept direct functions and domain adapters consistently.
|
|
149
|
+
*
|
|
150
|
+
* ## Ownership and lifetime
|
|
151
|
+
* This union acquires no resources; normalization does not extend the lifetime of either alternative.
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```ts
|
|
155
|
+
* import type { GuardInput } from "@typed/guard"
|
|
156
|
+
* import { liftPredicate } from "@typed/guard"
|
|
157
|
+
* const input: GuardInput<unknown, string> = liftPredicate((value: unknown): value is string => typeof value === "string")
|
|
158
|
+
* ```
|
|
159
|
+
*
|
|
160
|
+
* @category Models
|
|
43
161
|
* @since 1.0.0
|
|
44
162
|
*/
|
|
45
163
|
export type GuardInput<I, O, E = never, R = never> = Guard<I, O, E, R> | AsGuard<I, O, E, R>;
|
|
164
|
+
type RecordOutputConstraint<O> = O extends object ? O extends ReadonlyArray<unknown> ? never : unknown : never;
|
|
46
165
|
/**
|
|
166
|
+
* Returns a callable Guard unchanged or obtains one from an own callable
|
|
167
|
+
* `asGuard` property. Invalid adapter objects throw `TypeError` immediately.
|
|
168
|
+
*
|
|
169
|
+
* @remarks
|
|
170
|
+
* ## Why
|
|
171
|
+
* Central normalization makes invalid adapter shapes fail at construction instead of later during Effect execution.
|
|
172
|
+
*
|
|
173
|
+
* ## Ownership and lifetime
|
|
174
|
+
* Normalization acquires no resources and returns the existing Guard function or the adapter's result.
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* ```ts
|
|
178
|
+
* import { getGuard, liftPredicate } from "@typed/guard"
|
|
179
|
+
* const guard = getGuard(liftPredicate((value: unknown): value is string => typeof value === "string"))
|
|
180
|
+
* ```
|
|
181
|
+
*
|
|
182
|
+
* @category Constructors
|
|
47
183
|
* @since 1.0.0
|
|
48
184
|
*/
|
|
49
185
|
export declare const getGuard: <I, O, E = never, R = never>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, R>;
|
|
50
186
|
/**
|
|
187
|
+
* Runs `output` only when `input` matches. `None` short-circuits successfully,
|
|
188
|
+
* while failures and service requirements are preserved from both Guards.
|
|
189
|
+
*
|
|
190
|
+
* @remarks
|
|
191
|
+
* ## Why
|
|
192
|
+
* Sequential Guard composition must preserve ordinary non-match while unioning the typed error and service channels of both stages.
|
|
193
|
+
*
|
|
194
|
+
* ## Ownership and lifetime
|
|
195
|
+
* Construction acquires no resources; each invocation runs the first Effect and only starts the second after `Some`.
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* import { liftPredicate, pipe } from "@typed/guard"
|
|
200
|
+
* const nonEmpty = pipe(liftPredicate((u: unknown): u is string => typeof u === "string"), liftPredicate((s) => s.length > 0))
|
|
201
|
+
* ```
|
|
202
|
+
*
|
|
203
|
+
* @category Composition
|
|
51
204
|
* @since 1.0.0
|
|
52
205
|
*/
|
|
53
206
|
export declare const pipe: {
|
|
@@ -55,6 +208,19 @@ export declare const pipe: {
|
|
|
55
208
|
<I, O, E, R, B, E2, R2>(input: GuardInput<I, O, E, R>, output: GuardInput<O, B, E2, R2>): Guard<I, B, E | E2, R | R2>;
|
|
56
209
|
};
|
|
57
210
|
/**
|
|
211
|
+
* Maps matched output with an Effect while preserving `None`.
|
|
212
|
+
* @remarks
|
|
213
|
+
* ## Why
|
|
214
|
+
* Effectful mapping can add typed errors and services without changing non-match into failure.
|
|
215
|
+
* ## Ownership and lifetime
|
|
216
|
+
* Construction acquires no resources; the mapping Effect starts only after the source Guard produces `Some`.
|
|
217
|
+
* @example
|
|
218
|
+
* ```ts
|
|
219
|
+
* import { liftPredicate, mapEffect } from "@typed/guard"
|
|
220
|
+
* import { Effect } from "effect"
|
|
221
|
+
* const length = mapEffect(liftPredicate((u: unknown): u is string => typeof u === "string"), (s) => Effect.succeed(s.length))
|
|
222
|
+
* ```
|
|
223
|
+
* @category Composition
|
|
58
224
|
* @since 1.0.0
|
|
59
225
|
*/
|
|
60
226
|
export declare const mapEffect: {
|
|
@@ -62,6 +228,18 @@ export declare const mapEffect: {
|
|
|
62
228
|
<I, O, E, R, B, E2, R2>(guard: GuardInput<I, O, E, R>, f: (o: O) => Effect.Effect<B, E2, R2>): Guard<I, B, E | E2, R | R2>;
|
|
63
229
|
};
|
|
64
230
|
/**
|
|
231
|
+
* Maps matched output synchronously while preserving non-match and Effect channels.
|
|
232
|
+
* @remarks
|
|
233
|
+
* ## Why
|
|
234
|
+
* Pure output adaptation should not add errors or services and should never run for `None`.
|
|
235
|
+
* ## Ownership and lifetime
|
|
236
|
+
* Construction acquires no resources; the callback runs once for each `Some` during Effect execution.
|
|
237
|
+
* @example
|
|
238
|
+
* ```ts
|
|
239
|
+
* import { liftPredicate, map } from "@typed/guard"
|
|
240
|
+
* const length = map(liftPredicate((u: unknown): u is string => typeof u === "string"), (s) => s.length)
|
|
241
|
+
* ```
|
|
242
|
+
* @category Composition
|
|
65
243
|
* @since 1.0.0
|
|
66
244
|
*/
|
|
67
245
|
export declare const map: {
|
|
@@ -69,13 +247,40 @@ export declare const map: {
|
|
|
69
247
|
<I, O, E, R, B>(guard: GuardInput<I, O, E, R>, f: (o: O) => B): Guard<I, B, E, R>;
|
|
70
248
|
};
|
|
71
249
|
/**
|
|
250
|
+
* Runs a synchronous or Effectful observation for each matched value and returns the value unchanged.
|
|
251
|
+
* @remarks
|
|
252
|
+
* ## Why
|
|
253
|
+
* Observation composes without changing output, while Effect callbacks correctly contribute their errors and services.
|
|
254
|
+
* ## Ownership and lifetime
|
|
255
|
+
* Construction acquires no resources; callback lifetime is bounded by each matching Guard invocation.
|
|
256
|
+
* @example
|
|
257
|
+
* ```ts
|
|
258
|
+
* import { liftPredicate, tap } from "@typed/guard"
|
|
259
|
+
* const observed = tap(liftPredicate((u: unknown): u is string => typeof u === "string"), console.log)
|
|
260
|
+
* ```
|
|
261
|
+
* @category Composition
|
|
72
262
|
* @since 1.0.0
|
|
73
263
|
*/
|
|
74
264
|
export declare const tap: {
|
|
75
|
-
<O
|
|
76
|
-
<
|
|
265
|
+
<O>(f: (o: O) => void): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, R>;
|
|
266
|
+
<O, B, E2, R2>(f: (o: O) => Effect.Effect<B, E2, R2>): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E | E2, R | R2>;
|
|
267
|
+
<I, O, E, R>(guard: GuardInput<I, O, E, R>, f: (o: O) => void): Guard<I, O, E, R>;
|
|
268
|
+
<I, O, E, R, B, E2, R2>(guard: GuardInput<I, O, E, R>, f: (o: O) => Effect.Effect<B, E2, R2>): Guard<I, O, E | E2, R | R2>;
|
|
77
269
|
};
|
|
78
270
|
/**
|
|
271
|
+
* Refines and maps a matched value with an Option-returning function.
|
|
272
|
+
* @remarks
|
|
273
|
+
* ## Why
|
|
274
|
+
* `Option.none` provides a second ordinary non-match stage without introducing typed failure.
|
|
275
|
+
* ## Ownership and lifetime
|
|
276
|
+
* This combinator acquires no resources; the callback runs only for source matches.
|
|
277
|
+
* @example
|
|
278
|
+
* ```ts
|
|
279
|
+
* import { filterMap, liftPredicate } from "@typed/guard"
|
|
280
|
+
* import { Option } from "effect"
|
|
281
|
+
* const parsed = filterMap(liftPredicate((u: unknown): u is string => typeof u === "string"), (s) => s ? Option.some(Number(s)) : Option.none())
|
|
282
|
+
* ```
|
|
283
|
+
* @category Composition
|
|
79
284
|
* @since 1.0.0
|
|
80
285
|
*/
|
|
81
286
|
export declare const filterMap: {
|
|
@@ -83,24 +288,79 @@ export declare const filterMap: {
|
|
|
83
288
|
<I, O, E, R, B>(guard: GuardInput<I, O, E, R>, f: (o: O) => Option.Option<B>): Guard<I, B, E, R>;
|
|
84
289
|
};
|
|
85
290
|
/**
|
|
291
|
+
* Keeps matched values that satisfy a predicate or refinement.
|
|
292
|
+
* @remarks
|
|
293
|
+
* ## Why
|
|
294
|
+
* Predicate failure remains successful `None`, preserving Guard dispatch semantics and type refinement.
|
|
295
|
+
* ## Ownership and lifetime
|
|
296
|
+
* This combinator acquires no resources; the predicate runs only for source matches.
|
|
297
|
+
* @example
|
|
298
|
+
* ```ts
|
|
299
|
+
* import { filter, liftPredicate } from "@typed/guard"
|
|
300
|
+
* const positive = filter(liftPredicate((u: unknown): u is number => typeof u === "number"), (n) => n > 0)
|
|
301
|
+
* ```
|
|
302
|
+
* @category Composition
|
|
86
303
|
* @since 1.0.0
|
|
87
304
|
*/
|
|
88
305
|
export declare const filter: {
|
|
89
|
-
<O, O2 extends O>(predicate: (o: O) => o is O2): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I,
|
|
306
|
+
<O, O2 extends O>(predicate: (o: O) => o is O2): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O2, E, R>;
|
|
90
307
|
<O>(predicate: (o: O) => boolean): <I, R, E>(guard: GuardInput<I, O, E, R>) => Guard<I, O, E, R>;
|
|
91
|
-
<I, O, E, R, O2 extends O>(guard: GuardInput<I, O, E, R>, predicate: (o: O) => o is O2): Guard<I,
|
|
308
|
+
<I, O, E, R, O2 extends O>(guard: GuardInput<I, O, E, R>, predicate: (o: O) => o is O2): Guard<I, O2, E, R>;
|
|
92
309
|
<I, O, E, R>(guard: GuardInput<I, O, E, R>, predicate: (o: O) => boolean): Guard<I, O, E, R>;
|
|
93
310
|
};
|
|
94
311
|
/**
|
|
312
|
+
* Runs candidates sequentially and returns the first match tagged with its key.
|
|
313
|
+
* Candidates are snapshotted from own enumerable keys when `any` is called.
|
|
314
|
+
* ECMAScript own-key order applies: integer-index strings, other strings, then
|
|
315
|
+
* symbols.
|
|
316
|
+
*
|
|
317
|
+
* @remarks
|
|
318
|
+
* ## Why
|
|
319
|
+
* Ordered first-match dispatch turns independently composable Guards into a deterministic tagged union without treating `None` as failure.
|
|
320
|
+
*
|
|
321
|
+
* ## Ownership and lifetime
|
|
322
|
+
* Construction snapshots own enumerable entries and acquires no resources; each run executes candidates sequentially until the first `Some`.
|
|
323
|
+
*
|
|
324
|
+
* @example
|
|
325
|
+
* ```ts
|
|
326
|
+
* import { any, liftPredicate } from "@typed/guard"
|
|
327
|
+
* const classify = any({ text: liftPredicate((u: unknown): u is string => typeof u === "string") })
|
|
328
|
+
* ```
|
|
329
|
+
*
|
|
330
|
+
* @category Dispatch
|
|
95
331
|
* @since 1.0.0
|
|
96
332
|
*/
|
|
97
333
|
export declare function any<const GS extends Readonly<Record<string, GuardInput<any, any, any, any>>>>(guards: GS): Guard<AnyInput<GS>, AnyOutput<GS>, Guard.Error<GS[keyof GS]>, Guard.Services<GS[keyof GS]>>;
|
|
98
334
|
/**
|
|
335
|
+
* Computes the intersection of inputs accepted by an `any` Guard record.
|
|
336
|
+
* @remarks
|
|
337
|
+
* ## Why
|
|
338
|
+
* Every candidate receives the same runtime value, so the input must satisfy all candidate input contracts.
|
|
339
|
+
* ## Ownership and lifetime
|
|
340
|
+
* This compile-time type acquires no resources and has no runtime lifetime.
|
|
341
|
+
* @example
|
|
342
|
+
* ```ts
|
|
343
|
+
* import type { AnyInput, Guard } from "@typed/guard"
|
|
344
|
+
* type Input = AnyInput<{ text: Guard<unknown, string>; count: Guard<unknown, number> }>
|
|
345
|
+
* ```
|
|
346
|
+
* @category Type helpers
|
|
99
347
|
* @since 1.0.0
|
|
100
348
|
*/
|
|
101
349
|
export type AnyInput<GS extends Readonly<Record<string, GuardInput<any, any, any, any>>>> = UnionToIntersection<Guard.Input<GS[keyof GS]>>;
|
|
102
350
|
type UnionToIntersection<T> = (T extends any ? (x: T) => any : never) extends (x: infer R) => any ? R : never;
|
|
103
351
|
/**
|
|
352
|
+
* Builds the tagged output union produced by `any`.
|
|
353
|
+
* @remarks
|
|
354
|
+
* ## Why
|
|
355
|
+
* Preserving each record key in `_tag` lets downstream code narrow the winning Guard's value exhaustively.
|
|
356
|
+
* ## Ownership and lifetime
|
|
357
|
+
* This compile-time type acquires no resources and has no runtime lifetime.
|
|
358
|
+
* @example
|
|
359
|
+
* ```ts
|
|
360
|
+
* import type { AnyOutput, Guard } from "@typed/guard"
|
|
361
|
+
* type Output = AnyOutput<{ text: Guard<unknown, string>; count: Guard<unknown, number> }>
|
|
362
|
+
* ```
|
|
363
|
+
* @category Type helpers
|
|
104
364
|
* @since 1.0.0
|
|
105
365
|
*/
|
|
106
366
|
export type AnyOutput<GS extends Readonly<Record<string, GuardInput<any, any, any, any>>>> = [
|
|
@@ -112,11 +372,44 @@ export type AnyOutput<GS extends Readonly<Record<string, GuardInput<any, any, an
|
|
|
112
372
|
}[keyof GS]
|
|
113
373
|
] extends [infer R] ? R : never;
|
|
114
374
|
/**
|
|
375
|
+
* Builds a Guard from a predicate or refinement. The predicate is evaluated
|
|
376
|
+
* only when the returned Effect runs. A thrown exception becomes an Effect
|
|
377
|
+
* defect; use an effectful Guard when failure belongs in the typed error channel.
|
|
378
|
+
*
|
|
379
|
+
* @remarks
|
|
380
|
+
* ## Why
|
|
381
|
+
* Predicate lifting is the bridge from synchronous refinements into deferred Guard composition while preserving the `None` versus failure distinction.
|
|
382
|
+
*
|
|
383
|
+
* ## Ownership and lifetime
|
|
384
|
+
* Construction acquires no resources; the predicate runs once per invocation when the returned Effect executes.
|
|
385
|
+
*
|
|
386
|
+
* @example
|
|
387
|
+
* ```ts
|
|
388
|
+
* import { liftPredicate } from "@typed/guard"
|
|
389
|
+
* const string = liftPredicate((value: unknown): value is string => typeof value === "string")
|
|
390
|
+
* ```
|
|
391
|
+
*
|
|
392
|
+
* @category Constructors
|
|
115
393
|
* @since 1.0.0
|
|
116
394
|
*/
|
|
117
395
|
export declare function liftPredicate<A, B extends A>(predicate: Predicate.Refinement<A, B>): Guard<A, B>;
|
|
118
396
|
export declare function liftPredicate<A>(predicate: Predicate.Predicate<A>): Guard<A, A>;
|
|
119
397
|
/**
|
|
398
|
+
* Recovers from the complete Effect Cause and lifts the recovery result into `Some`.
|
|
399
|
+
* @remarks
|
|
400
|
+
* ## Why
|
|
401
|
+
* Cause-aware recovery can deliberately handle typed failures, defects, and interruption; `None` remains an unrecovered non-match.
|
|
402
|
+
* ## Ownership and lifetime
|
|
403
|
+
* Construction acquires no resources; recovery starts only when the source Effect fails and follows that invocation's lifetime.
|
|
404
|
+
* @example
|
|
405
|
+
* ```ts
|
|
406
|
+
* import { catchCause } from "@typed/guard"
|
|
407
|
+
* import type { Guard } from "@typed/guard"
|
|
408
|
+
* import { Effect } from "effect"
|
|
409
|
+
* const source: Guard<string, string, string> = (input) => input ? Effect.succeedSome(input) : Effect.fail("empty")
|
|
410
|
+
* const recovered = catchCause(source, () => Effect.succeed("fallback"))
|
|
411
|
+
* ```
|
|
412
|
+
* @category Error recovery
|
|
120
413
|
* @since 1.0.0
|
|
121
414
|
*/
|
|
122
415
|
export declare const catchCause: {
|
|
@@ -124,6 +417,21 @@ export declare const catchCause: {
|
|
|
124
417
|
<I, O, E, R, O2, E2, R2>(guard: GuardInput<I, O, E, R>, f: (e: Cause.Cause<E>) => Effect.Effect<O2, E2, R2>): Guard<I, O | O2, E2, R | R2>;
|
|
125
418
|
};
|
|
126
419
|
/**
|
|
420
|
+
* Recovers typed failures and lifts the recovery result into `Some`.
|
|
421
|
+
* @remarks
|
|
422
|
+
* ## Why
|
|
423
|
+
* Typed recovery leaves defects and interruption untouched and does not confuse successful `None` with failure. The exported `catch` name is an alias of this declaration.
|
|
424
|
+
* ## Ownership and lifetime
|
|
425
|
+
* Construction acquires no resources; recovery starts only for typed failure during the source invocation.
|
|
426
|
+
* @example
|
|
427
|
+
* ```ts
|
|
428
|
+
* import { catchAll } from "@typed/guard"
|
|
429
|
+
* import type { Guard } from "@typed/guard"
|
|
430
|
+
* import { Effect } from "effect"
|
|
431
|
+
* const source: Guard<string, string, string> = (input) => input ? Effect.succeedSome(input) : Effect.fail("empty")
|
|
432
|
+
* const recovered = catchAll(source, (error) => Effect.succeed(error.length))
|
|
433
|
+
* ```
|
|
434
|
+
* @category Error recovery
|
|
127
435
|
* @since 1.0.0
|
|
128
436
|
*/
|
|
129
437
|
export declare const catchAll: {
|
|
@@ -132,25 +440,46 @@ export declare const catchAll: {
|
|
|
132
440
|
};
|
|
133
441
|
export { catchAll as catch };
|
|
134
442
|
/**
|
|
443
|
+
* Recovers selected tagged typed failures and leaves unmatched tags in the error channel.
|
|
444
|
+
* @remarks
|
|
445
|
+
* ## Why
|
|
446
|
+
* Tag-specific recovery preserves type-safe residual errors while lifting recovered output into `Some`; `None`, defects, and interruption are unchanged.
|
|
447
|
+
* ## Ownership and lifetime
|
|
448
|
+
* Construction acquires no resources; the handler runs only for matching tagged failures during an invocation.
|
|
449
|
+
* @example
|
|
450
|
+
* ```ts
|
|
451
|
+
* import { catchTag } from "@typed/guard"
|
|
452
|
+
* import type { Guard } from "@typed/guard"
|
|
453
|
+
* import { Effect } from "effect"
|
|
454
|
+
* type NotFound = { readonly _tag: "NotFound" }
|
|
455
|
+
* const source: Guard<string, string, NotFound> = (input) => input ? Effect.succeedSome(input) : Effect.fail({ _tag: "NotFound" })
|
|
456
|
+
* const recovered = catchTag(source, "NotFound", () => Effect.succeed("fallback"))
|
|
457
|
+
* ```
|
|
458
|
+
* @category Error recovery
|
|
135
459
|
* @since 1.0.0
|
|
136
460
|
*/
|
|
137
461
|
export declare const catchTag: {
|
|
138
|
-
<E, K extends E extends
|
|
139
|
-
|
|
140
|
-
} ? E["_tag"] : never, O2, E2, R2>(tag: K, f: (e: Extract<E, {
|
|
141
|
-
_tag: K;
|
|
142
|
-
}>) => Effect.Effect<O2, E2, R2>): <I, O, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O | O2, E2 | Exclude<E, {
|
|
143
|
-
_tag: K;
|
|
144
|
-
}>, R | R2>;
|
|
145
|
-
<I, O, E, R, K extends E extends {
|
|
146
|
-
_tag: string;
|
|
147
|
-
} ? E["_tag"] : never, O2, E2, R2>(guard: GuardInput<I, O, E, R>, tag: K, f: (e: Extract<E, {
|
|
148
|
-
_tag: K;
|
|
149
|
-
}>) => Effect.Effect<O2, E2, R2>): Guard<I, O | O2, E2 | Exclude<E, {
|
|
150
|
-
_tag: K;
|
|
151
|
-
}>, R | R2>;
|
|
462
|
+
<const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>, E, O2, E2, R2>(tag: K, f: (e: ExtractTag<NoInfer<E>, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Effect.Effect<O2, E2, R2>): <I, O, R>(guard: GuardInput<I, O, E, R>) => Guard<I, O | O2, E2 | ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>, R | R2>;
|
|
463
|
+
<I, O, E, R, const K extends Tags<E> | Arr.NonEmptyReadonlyArray<Tags<E>>, O2, E2, R2>(guard: GuardInput<I, O, E, R>, tag: K, f: (e: ExtractTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>) => Effect.Effect<O2, E2, R2>): Guard<I, O | O2, E2 | ExcludeTag<E, K extends Arr.NonEmptyReadonlyArray<string> ? K[number] : K>, R | R2>;
|
|
152
464
|
};
|
|
153
465
|
/**
|
|
466
|
+
* Provides a Context or Layer to a Guard's Effect.
|
|
467
|
+
* @remarks
|
|
468
|
+
* ## Why
|
|
469
|
+
* Provision removes supplied services from `R`; Layers may also contribute acquisition errors and their own requirements exactly as Effect does.
|
|
470
|
+
* ## Ownership and lifetime
|
|
471
|
+
* A Context acquires no resources here. Layer resources are acquired and released according to the Effect Scope that runs the Guard.
|
|
472
|
+
* @example
|
|
473
|
+
* ```ts
|
|
474
|
+
* import { provide } from "@typed/guard"
|
|
475
|
+
* import type { Guard } from "@typed/guard"
|
|
476
|
+
* import { Context, Effect, Option } from "effect"
|
|
477
|
+
* const Flag = Context.Service<{ readonly enabled: boolean }>("Flag")
|
|
478
|
+
* const requiresFlag: Guard<boolean, boolean, never, Context.Service.Identifier<typeof Flag>> = (input) => Effect.map(Effect.service(Flag), ({ enabled }) => enabled && input ? Option.some(input) : Option.none())
|
|
479
|
+
* const guard = provide(requiresFlag, Context.make(Flag, { enabled: true }))
|
|
480
|
+
* ```
|
|
481
|
+
* See [Effect services and Layers](https://effect.website/docs/requirements-management/layers/).
|
|
482
|
+
* @category Services
|
|
154
483
|
* @since 1.0.0
|
|
155
484
|
*/
|
|
156
485
|
export declare const provide: {
|
|
@@ -160,6 +489,22 @@ export declare const provide: {
|
|
|
160
489
|
<I, O, E, R, R2, E2, R3>(guard: GuardInput<I, O, E, R>, provided: Layer.Layer<R2, E2, R3>): Guard<I, O, E | E2, Exclude<R, R2> | R3>;
|
|
161
490
|
};
|
|
162
491
|
/**
|
|
492
|
+
* Provides one concrete service to a Guard.
|
|
493
|
+
* @remarks
|
|
494
|
+
* ## Why
|
|
495
|
+
* Targeted service provision removes only the selected identifier from the Guard environment.
|
|
496
|
+
* ## Ownership and lifetime
|
|
497
|
+
* This combinator acquires no resources and reuses the supplied service for each invocation.
|
|
498
|
+
* @example
|
|
499
|
+
* ```ts
|
|
500
|
+
* import { provideService } from "@typed/guard"
|
|
501
|
+
* import type { Guard } from "@typed/guard"
|
|
502
|
+
* import { Context, Effect, Option } from "effect"
|
|
503
|
+
* const Flag = Context.Service<{ readonly enabled: boolean }>("Flag")
|
|
504
|
+
* const requiresFlag: Guard<boolean, boolean, never, Context.Service.Identifier<typeof Flag>> = (input) => Effect.map(Effect.service(Flag), ({ enabled }) => enabled && input ? Option.some(input) : Option.none())
|
|
505
|
+
* const guard = provideService(requiresFlag, Flag, { enabled: true })
|
|
506
|
+
* ```
|
|
507
|
+
* @category Services
|
|
163
508
|
* @since 1.0.0
|
|
164
509
|
*/
|
|
165
510
|
export declare const provideService: {
|
|
@@ -167,6 +512,22 @@ export declare const provideService: {
|
|
|
167
512
|
<I, O, E, R, Id, S>(guard: GuardInput<I, O, E, R>, tag: Context.Service<Id, S>, service: S): Guard<I, O, E, Exclude<R, Id>>;
|
|
168
513
|
};
|
|
169
514
|
/**
|
|
515
|
+
* Provides a service produced by an Effect to a Guard.
|
|
516
|
+
* @remarks
|
|
517
|
+
* ## Why
|
|
518
|
+
* Effectful provision makes acquisition errors and required services explicit in the composed Guard channels.
|
|
519
|
+
* ## Ownership and lifetime
|
|
520
|
+
* The service Effect runs for each Guard invocation and is interrupted with that invocation; scoped resources follow the surrounding Scope.
|
|
521
|
+
* @example
|
|
522
|
+
* ```ts
|
|
523
|
+
* import { provideServiceEffect } from "@typed/guard"
|
|
524
|
+
* import type { Guard } from "@typed/guard"
|
|
525
|
+
* import { Context, Effect, Option } from "effect"
|
|
526
|
+
* const Flag = Context.Service<{ readonly enabled: boolean }>("Flag")
|
|
527
|
+
* const requiresFlag: Guard<boolean, boolean, never, Context.Service.Identifier<typeof Flag>> = (input) => Effect.map(Effect.service(Flag), ({ enabled }) => enabled && input ? Option.some(input) : Option.none())
|
|
528
|
+
* const guard = provideServiceEffect(requiresFlag, Flag, Effect.succeed({ enabled: true }))
|
|
529
|
+
* ```
|
|
530
|
+
* @category Services
|
|
170
531
|
* @since 1.0.0
|
|
171
532
|
*/
|
|
172
533
|
export declare const provideServiceEffect: {
|
|
@@ -174,55 +535,159 @@ export declare const provideServiceEffect: {
|
|
|
174
535
|
<I, O, E, R, Id, S, E2, R2>(guard: GuardInput<I, O, E, R>, tag: Context.Service<Id, S>, service: Effect.Effect<S, E2, R2>): Guard<I, O, E | E2, Exclude<R, Id> | R2>;
|
|
175
536
|
};
|
|
176
537
|
/**
|
|
538
|
+
* Creates a Guard that decodes a schema's Encoded input to its Type.
|
|
539
|
+
* @remarks
|
|
540
|
+
* ## Why
|
|
541
|
+
* Schema decoding reports all issues, ignores excess properties, retains `SchemaError` in the typed channel, and carries decoding services in `R`.
|
|
542
|
+
* ## Ownership and lifetime
|
|
543
|
+
* Construction acquires no resources; schema services and any scoped work follow each returned Effect invocation.
|
|
544
|
+
* @example
|
|
545
|
+
* ```ts
|
|
546
|
+
* import { fromSchemaDecode } from "@typed/guard"
|
|
547
|
+
* import { Schema } from "effect"
|
|
548
|
+
* const number = fromSchemaDecode(Schema.NumberFromString)
|
|
549
|
+
* ```
|
|
550
|
+
* See [Effect Schema transformations](https://effect.website/docs/schema/transformations/).
|
|
551
|
+
* @category Schema
|
|
177
552
|
* @since 1.0.0
|
|
178
553
|
*/
|
|
179
554
|
export declare function fromSchemaDecode<S extends Schema.Top>(schema: S): Guard<S["Encoded"], S["Type"], Schema.SchemaError, S["DecodingServices"]>;
|
|
180
555
|
/**
|
|
556
|
+
* Creates a Guard that encodes a schema's Type to its Encoded representation.
|
|
557
|
+
* @remarks
|
|
558
|
+
* ## Why
|
|
559
|
+
* Schema encoding is the reverse direction of decoding and keeps encoding services and `SchemaError` visible in the Guard type.
|
|
560
|
+
* ## Ownership and lifetime
|
|
561
|
+
* Construction acquires no resources; schema services and any scoped work follow each returned Effect invocation.
|
|
562
|
+
* @example
|
|
563
|
+
* ```ts
|
|
564
|
+
* import { fromSchemaEncode } from "@typed/guard"
|
|
565
|
+
* import { Schema } from "effect"
|
|
566
|
+
* const encoded = fromSchemaEncode(Schema.NumberFromString)
|
|
567
|
+
* ```
|
|
568
|
+
* @category Schema
|
|
181
569
|
* @since 1.0.0
|
|
182
570
|
*/
|
|
183
571
|
export declare function fromSchemaEncode<S extends Schema.Top>(schema: S): Guard<S["Type"], S["Encoded"], Schema.SchemaError, S["EncodingServices"]>;
|
|
184
572
|
/**
|
|
573
|
+
* Decodes each matched encoded value through an Effect Schema.
|
|
574
|
+
* @remarks
|
|
575
|
+
* ## Why
|
|
576
|
+
* The composed Guard preserves source non-match and errors while adding schema failures and decoding service requirements.
|
|
577
|
+
* ## Ownership and lifetime
|
|
578
|
+
* Construction acquires no resources; decoding starts only for `Some` and follows that invocation's lifetime.
|
|
579
|
+
* @example
|
|
580
|
+
* ```ts
|
|
581
|
+
* import { decode, liftPredicate } from "@typed/guard"
|
|
582
|
+
* import { Schema } from "effect"
|
|
583
|
+
* const number = decode(liftPredicate((u: unknown): u is string => typeof u === "string"), Schema.NumberFromString)
|
|
584
|
+
* ```
|
|
585
|
+
* @category Schema
|
|
185
586
|
* @since 1.0.0
|
|
186
587
|
*/
|
|
187
588
|
export declare const decode: {
|
|
188
|
-
<S extends Schema.Top>(schema: S): <I, E = never, R = never>(guard: GuardInput<I, S["
|
|
189
|
-
<I,
|
|
589
|
+
<S extends Schema.Top>(schema: S): <I, E = never, R = never>(guard: GuardInput<I, S["Encoded"], E, R>) => Guard<I, S["Type"], Schema.SchemaError | E, R | S["DecodingServices"]>;
|
|
590
|
+
<I, E, R, S extends Schema.Top>(guard: GuardInput<I, S["Encoded"], E, R>, schema: S): Guard<I, S["Type"], Schema.SchemaError | E, R | S["DecodingServices"]>;
|
|
190
591
|
};
|
|
191
592
|
/**
|
|
593
|
+
* Encodes each matched schema Type to its Encoded representation.
|
|
594
|
+
* @remarks
|
|
595
|
+
* ## Why
|
|
596
|
+
* The composed Guard preserves source non-match and errors while adding schema failures and encoding service requirements.
|
|
597
|
+
* ## Ownership and lifetime
|
|
598
|
+
* Construction acquires no resources; encoding starts only for `Some` and follows that invocation's lifetime.
|
|
599
|
+
* @example
|
|
600
|
+
* ```ts
|
|
601
|
+
* import { encode, liftPredicate } from "@typed/guard"
|
|
602
|
+
* import { Schema } from "effect"
|
|
603
|
+
* const text = encode(liftPredicate((u: unknown): u is number => typeof u === "number"), Schema.NumberFromString)
|
|
604
|
+
* ```
|
|
605
|
+
* @category Schema
|
|
192
606
|
* @since 1.0.0
|
|
193
607
|
*/
|
|
194
608
|
export declare const encode: {
|
|
195
|
-
<S extends Schema.Top>(schema: S): <I, E = never, R = never>(guard: GuardInput<I, S["Type"], E, R>) => Guard<I, S["
|
|
196
|
-
<I,
|
|
609
|
+
<S extends Schema.Top>(schema: S): <I, E = never, R = never>(guard: GuardInput<I, S["Type"], E, R>) => Guard<I, S["Encoded"], Schema.SchemaError | E, R | S["EncodingServices"]>;
|
|
610
|
+
<I, E, R, S extends Schema.Top>(guard: GuardInput<I, S["Type"], E, R>, schema: S): Guard<I, S["Encoded"], Schema.SchemaError | E, R | S["EncodingServices"]>;
|
|
197
611
|
};
|
|
198
612
|
/**
|
|
613
|
+
* Adds a fixed property to every matched record output.
|
|
614
|
+
* @remarks
|
|
615
|
+
* ## Why
|
|
616
|
+
* Record construction does not mutate the input and rejects existing enumerable keys; it returns an unfrozen plain object containing own enumerable string and symbol properties while dropping prototypes and non-enumerables.
|
|
617
|
+
* ## Ownership and lifetime
|
|
618
|
+
* This pure combinator acquires no resources and creates a fresh plain object for every match.
|
|
619
|
+
* @example
|
|
620
|
+
* ```ts
|
|
621
|
+
* import { bindTo, let as letGuard, liftPredicate } from "@typed/guard"
|
|
622
|
+
* const named = letGuard(bindTo(liftPredicate(Boolean), "value"), "kind", "input")
|
|
623
|
+
* ```
|
|
624
|
+
* @category Record construction
|
|
199
625
|
* @since 1.0.0
|
|
200
626
|
*/
|
|
201
627
|
declare const let_: {
|
|
202
|
-
<K extends PropertyKey, B>(key: K, value: B): <
|
|
628
|
+
<K extends PropertyKey, B>(key: K, value: B): <G extends GuardInput<any, any, any, any>>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>> & (K extends NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never> ? never : unknown)) => Guard<Guard.Input<G>, Guard.Output<G> & {
|
|
203
629
|
[k in K]: B;
|
|
204
|
-
},
|
|
205
|
-
<
|
|
630
|
+
}, Guard.Error<G>, Guard.Services<G>>;
|
|
631
|
+
<G extends GuardInput<any, any, any, any>, K extends PropertyKey, B>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>>, key: Exclude<K, NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never>>, value: B): Guard<Guard.Input<G>, Guard.Output<G> & {
|
|
206
632
|
[k in K]: B;
|
|
207
|
-
},
|
|
633
|
+
}, Guard.Error<G>, Guard.Services<G>>;
|
|
208
634
|
};
|
|
209
635
|
export {
|
|
210
636
|
/**
|
|
637
|
+
* Adds a fixed property to every matched object output. The key must not
|
|
638
|
+
* already exist; use `bindTo` first when the prior output is not an object.
|
|
639
|
+
*
|
|
640
|
+
* This alias shares the canonical documentation and behavior of `let_`.
|
|
641
|
+
*
|
|
211
642
|
* @since 1.0.0
|
|
212
643
|
*/
|
|
213
644
|
let_ as let, };
|
|
214
645
|
/**
|
|
646
|
+
* Adds a readonly `_tag` to every matched object output. The output must not
|
|
647
|
+
* already have an `_tag` property.
|
|
648
|
+
*
|
|
649
|
+
* @remarks
|
|
650
|
+
* ## Why
|
|
651
|
+
* A typed discriminant turns matched record outputs into exhaustively narrowable tagged values while rejecting key collisions.
|
|
652
|
+
*
|
|
653
|
+
* ## Ownership and lifetime
|
|
654
|
+
* This pure combinator acquires no resources and creates a fresh plain object for every match.
|
|
655
|
+
*
|
|
656
|
+
* @example
|
|
657
|
+
* ```ts
|
|
658
|
+
* import { addTag, bindTo, liftPredicate } from "@typed/guard"
|
|
659
|
+
* const tagged = addTag(bindTo(liftPredicate(Boolean), "value"), "Input")
|
|
660
|
+
* ```
|
|
661
|
+
*
|
|
662
|
+
* @category Record construction
|
|
215
663
|
* @since 1.0.0
|
|
216
664
|
*/
|
|
217
665
|
export declare const addTag: {
|
|
218
|
-
<B>(value: B): <
|
|
666
|
+
<B>(value: B): <G extends GuardInput<any, any, any, any>>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>> & ("_tag" extends NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never> ? never : unknown)) => Guard<Guard.Input<G>, Guard.Output<G> & {
|
|
219
667
|
readonly _tag: B;
|
|
220
|
-
},
|
|
221
|
-
<
|
|
668
|
+
}, Guard.Error<G>, Guard.Services<G>>;
|
|
669
|
+
<G extends GuardInput<any, any, any, any>, B>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>> & ("_tag" extends NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never> ? never : unknown), value: B): Guard<Guard.Input<G>, Guard.Output<G> & {
|
|
222
670
|
readonly _tag: B;
|
|
223
|
-
},
|
|
671
|
+
}, Guard.Error<G>, Guard.Services<G>>;
|
|
224
672
|
};
|
|
225
673
|
/**
|
|
674
|
+
* Wraps any matched output in a new object under `key`. This is the explicit
|
|
675
|
+
* transition from an arbitrary output to the record-building workflow.
|
|
676
|
+
*
|
|
677
|
+
* @remarks
|
|
678
|
+
* ## Why
|
|
679
|
+
* Explicit wrapping makes arbitrary values safe for record combinators without assuming their runtime shape.
|
|
680
|
+
*
|
|
681
|
+
* ## Ownership and lifetime
|
|
682
|
+
* This pure combinator acquires no resources and creates a fresh plain object for every match.
|
|
683
|
+
*
|
|
684
|
+
* @example
|
|
685
|
+
* ```ts
|
|
686
|
+
* import { bindTo, liftPredicate } from "@typed/guard"
|
|
687
|
+
* const named = bindTo(liftPredicate(Boolean), "value")
|
|
688
|
+
* ```
|
|
689
|
+
*
|
|
690
|
+
* @category Record construction
|
|
226
691
|
* @since 1.0.0
|
|
227
692
|
*/
|
|
228
693
|
export declare const bindTo: {
|
|
@@ -234,14 +699,34 @@ export declare const bindTo: {
|
|
|
234
699
|
}, E, R>;
|
|
235
700
|
};
|
|
236
701
|
/**
|
|
702
|
+
* Runs `f` on a matched object and adds its matched value under a new key. The
|
|
703
|
+
* key must not already exist. Enumerable getters and proxy traps may execute
|
|
704
|
+
* during the object spread.
|
|
705
|
+
*
|
|
706
|
+
* @remarks
|
|
707
|
+
* ## Why
|
|
708
|
+
* Dependent record construction runs the second Guard only after the base matches, preserves `None`, and unions both error and service channels.
|
|
709
|
+
*
|
|
710
|
+
* ## Ownership and lifetime
|
|
711
|
+
* Construction acquires no resources; each successful bind creates a fresh plain object and the dependent Effect follows the invocation lifetime.
|
|
712
|
+
*
|
|
713
|
+
* @example
|
|
714
|
+
* ```ts
|
|
715
|
+
* import { bind, bindTo, liftPredicate, map } from "@typed/guard"
|
|
716
|
+
* const base = bindTo(liftPredicate((u: unknown): u is string => typeof u === "string"), "text")
|
|
717
|
+
* const nonEmptyLength = map(liftPredicate((record: { readonly text: string }) => record.text.length > 0), (record) => record.text.length)
|
|
718
|
+
* const sized = bind(base, "length", nonEmptyLength)
|
|
719
|
+
* ```
|
|
720
|
+
*
|
|
721
|
+
* @category Record construction
|
|
237
722
|
* @since 1.0.0
|
|
238
723
|
*/
|
|
239
724
|
export declare const bind: {
|
|
240
|
-
<
|
|
725
|
+
<O extends object, K extends PropertyKey, B, E2, R2>(key: K, f: GuardInput<O, B, E2, R2>): <G extends GuardInput<any, O, any, any>>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>> & (K extends NoInfer<Guard.Output<G> extends infer A ? (A extends unknown ? keyof A : never) : never> ? never : unknown)) => Guard<Guard.Input<G>, Guard.Output<G> & {
|
|
241
726
|
[k in K]: B;
|
|
242
|
-
},
|
|
243
|
-
<
|
|
727
|
+
}, Guard.Error<G> | E2, Guard.Services<G> | R2>;
|
|
728
|
+
<G extends GuardInput<any, any, any, any>, K extends PropertyKey, B, E2, R2>(guard: G & RecordOutputConstraint<NoInfer<Guard.Output<G>>>, key: Exclude<K, NoInfer<Guard.Output<G> extends infer O ? (O extends unknown ? keyof O : never) : never>>, f: GuardInput<NoInfer<Guard.Output<G>>, B, E2, R2>): Guard<Guard.Input<G>, Guard.Output<G> & {
|
|
244
729
|
[k in K]: B;
|
|
245
|
-
},
|
|
730
|
+
}, Guard.Error<G> | E2, Guard.Services<G> | R2>;
|
|
246
731
|
};
|
|
247
732
|
//# sourceMappingURL=index.d.ts.map
|