1/**
2 * Defines helpers for small immutable data models.
3 *
4 * This module helps create plain classes, tagged classes, tagged unions, and
5 * typed errors with readonly fields. Tagged values carry a `_tag` field, which
6 * makes them easy to narrow with pattern matching or simple checks. These
7 * helpers are commonly used for domain values and errors in Effect programs.
8 *
9 * @since 2.0.0
10 */
11import type * as Cause from "./Cause.ts"
12import * as core from "./internal/core.ts"
13import * as InternalRecord from "./internal/record.ts"
14import * as Pipeable from "./Pipeable.ts"
15import * as Predicate from "./Predicate.ts"
16import type * as Types from "./Types.ts"
17import type { Unify } from "./Unify.ts"
18
19/**
20 * Provides a base class for immutable data types.
21 *
22 * **When to use**
23 *
24 * Use when you need a lightweight immutable value type with `.pipe()` support.
25 *
26 * **Details**
27 *
28 * Extend `Class` with a type parameter to declare fields. The constructor
29 * accepts those fields as a single object argument. When there are no fields
30 * the argument is optional. Instances are `Readonly` and `Pipeable`.
31 *
32 * **Example** (Defining a value class)
33 *
34 * ```ts import.meta.vitest
35 * import { Data, Equal } from "effect"
36 *
37 * class Person extends Data.Class<{ readonly name: string }> {}
38 *
39 * Equal.equals(new Person({ name: "Mike" }), new Person({ name: "Mike" })) // => true
40 * ```
41 *
42 * @see {@link TaggedClass} — adds a `_tag` field
43 * @see {@link Error} — yieldable error variant
44 *
45 * @category constructors
46 * @since 2.0.0
47 */
48export const Class: new<A extends Record<string, any> = {}>(
49 args: Types.VoidIfEmpty<{ readonly [P in keyof A]: A[P] }>
50) => Readonly<A> & Pipeable.Pipeable = class extends Pipeable.Class {
51 constructor(props: object | undefined) {
52 super()
53 if (props) {
54 InternalRecord.assignProperties(this, props)
55 }
56 }
57} as any
58
59/**
60 * Provides a base class for immutable data types with a `_tag` discriminator.
61 *
62 * **When to use**
63 *
64 * Use when you need a single-variant tagged type or an ad-hoc discriminator.
65 *
66 * **Details**
67 *
68 * Like {@link Class}, but the resulting instances also carry a
69 * `readonly _tag: Tag` property. The `_tag` is excluded from the constructor
70 * argument.
71 *
72 * **Example** (Defining a tagged class)
73 *
74 * ```ts import.meta.vitest
75 * import { Data } from "effect"
76 *
77 * class Person extends Data.TaggedClass("Person")<{
78 * readonly name: string
79 * }> {}
80 *
81 * new Person({ name: "Mike" })._tag // => "Person"
82 * ```
83 *
84 * @see {@link Class} — without a `_tag`
85 * @see {@link TaggedError} — tagged error variant
86 * @see {@link TaggedEnum} — multi-variant unions
87 *
88 * @category constructors
89 * @since 2.0.0
90 */
91export const TaggedClass = <Tag extends string>(
92 tag: Tag
93): new<A extends Record<string, any> = {}>(
94 args: Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>
95) => Readonly<A> & { readonly _tag: Tag } & Pipeable.Pipeable =>
96 class extends Class {
97 readonly _tag = tag
98 } as any
99
100/**
101 * Transforms a record of variant definitions into a discriminated union type.
102 *
103 * **When to use**
104 *
105 * Use when you have two or more variants that share a common `_tag` discriminator.
106 *
107 * **Details**
108 *
109 * Each key in the record becomes a variant with `readonly _tag` set to that
110 * key. Use with {@link taggedEnum} to get constructors and matchers.
111 *
112 * **Gotchas**
113 *
114 * Variant records must **not** include a `_tag` property; it is added automatically.
115 *
116 * **Example** (Defining a tagged enum)
117 *
118 * ```ts import.meta.vitest
119 * import { Data } from "effect"
120 *
121 * type HttpError = Data.TaggedEnum<{
122 * BadRequest: { readonly status: 400; readonly message: string }
123 * NotFound: { readonly status: 404 }
124 * }>
125 *
126 * // Equivalent to:
127 * // | { readonly _tag: "BadRequest"; readonly status: 400; readonly message: string }
128 * // | { readonly _tag: "NotFound"; readonly status: 404 }
129 *
130 * const { BadRequest, NotFound } = Data.taggedEnum<HttpError>()
131 *
132 * BadRequest({ status: 400, message: "missing id" })._tag // => "BadRequest"
133 * ```
134 *
135 * @see {@link taggedEnum} — constructors and matchers for a `TaggedEnum`
136 * @see {@link TaggedEnum.WithGenerics} — generic tagged enums
137 * @see {@link TaggedEnum.Constructor} — the constructor object type
138 *
139 * @category models
140 * @since 2.0.0
141 */
142export type TaggedEnum<
143 A extends Record<string, Record<string, any>> & UntaggedChildren<A>
144> = keyof A extends infer Tag ? Tag extends keyof A ? Types.Simplify<
145 { readonly _tag: Tag } & { readonly [K in keyof A[Tag]]: A[Tag][K] }
146 >
147 : never
148 : never
149
150type ChildrenAreTagged<A> = keyof A extends infer K ? K extends keyof A ? "_tag" extends keyof A[K] ? true
151 : false
152 : never
153 : never
154
155type UntaggedChildren<A> = true extends ChildrenAreTagged<A>
156 ? "It looks like you're trying to create a tagged enum, but one or more of its members already has a `_tag` property."
157 : unknown
158
159/**
160 * Namespace for `TaggedEnum` utility types.
161 *
162 * **When to use**
163 *
164 * Use to reference utility types for constructing, extracting, and matching
165 * `TaggedEnum` variants.
166 *
167 * **Details**
168 *
169 * Provides helper types for:
170 * - Generic tagged enums ({@link TaggedEnum.WithGenerics}, {@link TaggedEnum.Kind})
171 * - Extracting constructor arguments ({@link TaggedEnum.Args}) and variant
172 * values ({@link TaggedEnum.Value})
173 * - Full constructor objects ({@link TaggedEnum.Constructor})
174 *
175 * @since 2.0.0
176 */
177export declare namespace TaggedEnum {
178 /**
179 * Defines a tagged enum shape that accepts generic type parameters.
180 *
181 * **When to use**
182 *
183 * Use when variant payloads need to be parameterized, such as `Result<E, A>`.
184 *
185 * **Details**
186 *
187 * Extend this interface and set `taggedEnum` to your union type, using
188 * `this["A"]`, `this["B"]`, etc. as placeholders for the generics. The
189 * `Count` parameter declares how many generics are used (up to 4).
190 *
191 * **Example** (Defining a generic tagged enum)
192 *
193 * ```ts import.meta.vitest
194 * import { Data } from "effect"
195 *
196 * type MyResult<E, A> = Data.TaggedEnum<{
197 * Failure: { readonly error: E }
198 * Success: { readonly value: A }
199 * }>
200 *
201 * interface MyResultDef extends Data.TaggedEnum.WithGenerics<2> {
202 * readonly taggedEnum: MyResult<this["A"], this["B"]>
203 * }
204 *
205 * const { Failure, Success } = Data.taggedEnum<MyResultDef>()
206 *
207 * const ok = Success({ value: 42 })
208 * // ok: { readonly _tag: "Success"; readonly value: number }
209 * ok // => { value: 42, _tag: "Success" }
210 * ```
211 *
212 * @see {@link Kind} — apply concrete types to a `WithGenerics` definition
213 * @see {@link taggedEnum} — constructors and matchers
214 *
215 * @category models
216 * @since 2.0.0
217 */
218 export interface WithGenerics<Count extends number> {
219 readonly taggedEnum: { readonly _tag: string }
220 readonly numberOfGenerics: Count
221
222 readonly A: unknown
223 readonly B: unknown
224 readonly C: unknown
225 readonly D: unknown
226 }
227
228 /**
229 * Applies concrete type arguments to a `WithGenerics` definition, producing
230 * the resulting tagged union type.
231 *
232 * **When to use**
233 *
234 * Use to refer to a specific instantiation of a generic tagged enum in type signatures.
235 *
236 * **Example** (Applying generics)
237 *
238 * ```ts import.meta.vitest
239 * import type { Data } from "effect"
240 *
241 * type Option<A> = Data.TaggedEnum<{
242 * None: {}
243 * Some: { readonly value: A }
244 * }>
245 * interface OptionDef extends Data.TaggedEnum.WithGenerics<1> {
246 * readonly taggedEnum: Option<this["A"]>
247 * }
248 *
249 * // Resolves to the concrete union for `string`:
250 * // { _tag: "None" } | { _tag: "Some"; value: string }
251 * type StringOption = Data.TaggedEnum.Kind<OptionDef, string>
252 * ```
253 *
254 * @see {@link WithGenerics} — define the generic shape
255 *
256 * @category utility types
257 * @since 2.0.0
258 */
259 export type Kind<
260 Z extends WithGenerics<number>,
261 A = unknown,
262 B = unknown,
263 C = unknown,
264 D = unknown
265 > = (Z & {
266 readonly A: A
267 readonly B: B
268 readonly C: C
269 readonly D: D
270 })["taggedEnum"]
271
272 /**
273 * Extracts the constructor argument type for a specific variant of a tagged
274 * union.
275 *
276 * **When to use**
277 *
278 * Use to derive the argument object expected by a constructor for one tagged
279 * union variant.
280 *
281 * **Details**
282 *
283 * Returns `void` if the variant has no fields beyond `_tag`.
284 *
285 * **Example** (Extracting variant args)
286 *
287 * ```ts import.meta.vitest
288 * import type { Data } from "effect"
289 *
290 * type Result =
291 * | { readonly _tag: "Ok"; readonly value: number }
292 * | { readonly _tag: "Err"; readonly error: string }
293 *
294 * type OkArgs = Data.TaggedEnum.Args<Result, "Ok">
295 * // { readonly value: number }
296 *
297 * type ErrArgs = Data.TaggedEnum.Args<Result, "Err">
298 * // { readonly error: string }
299 * ```
300 *
301 * @see {@link Value} — extracts the full variant type (including `_tag`)
302 *
303 * @category utility types
304 * @since 2.0.0
305 */
306 export type Args<
307 A extends { readonly _tag: string },
308 K extends A["_tag"],
309 E = Extract<A, { readonly _tag: K }>
310 > = {
311 readonly [K in keyof E as K extends "_tag" ? never : K]: E[K]
312 } extends infer T ? Types.VoidIfEmpty<T>
313 : never
314
315 /**
316 * Extracts the full variant type (including `_tag`) for a specific tag.
317 *
318 * **When to use**
319 *
320 * Use to select one full tagged-union variant by its `_tag` value.
321 *
322 * **Example** (Extracting a variant type)
323 *
324 * ```ts import.meta.vitest
325 * import type { Data } from "effect"
326 *
327 * type Result =
328 * | { readonly _tag: "Ok"; readonly value: number }
329 * | { readonly _tag: "Err"; readonly error: string }
330 *
331 * type OkVariant = Data.TaggedEnum.Value<Result, "Ok">
332 * // { readonly _tag: "Ok"; readonly value: number }
333 * ```
334 *
335 * @see {@link Args} — extracts fields without `_tag`
336 *
337 * @category utility types
338 * @since 2.0.0
339 */
340 export type Value<
341 A extends { readonly _tag: string },
342 K extends A["_tag"]
343 > = Extract<A, { readonly _tag: K }>
344
345 /**
346 * The full constructors-and-matchers object type returned by {@link taggedEnum}.
347 *
348 * **When to use**
349 *
350 * Use when you want to annotate an exported constructor bundle so downstream
351 * code keeps exact variant constructors and exhaustive matching.
352 *
353 * **Details**
354 *
355 * Includes:
356 * - A constructor function for each variant (keyed by tag name)
357 * - `$is(tag)` — returns a type-guard that checks only the `_tag` field;
358 * safe when the tag is globally unique and the value was produced by your
359 * constructors. For untrusted input, validate with the `Schema` module first.
360 * - `$match` — exhaustive pattern matching (data-last or data-first)
361 *
362 * **Example** (Using the constructor object)
363 *
364 * ```ts import.meta.vitest
365 * import { Data } from "effect"
366 *
367 * type Shape =
368 * | { readonly _tag: "Circle"; readonly radius: number }
369 * | { readonly _tag: "Rect"; readonly w: number; readonly h: number }
370 *
371 * const { Circle, Rect, $is, $match } = Data.taggedEnum<Shape>()
372 *
373 * const shape = Circle({ radius: 10 })
374 *
375 * if ($is("Circle")(shape)) {
376 * shape.radius // => 10
377 * }
378 *
379 * $match(shape, {
380 * Circle: (s) => `circle r=${s.radius}`,
381 * Rect: (s) => `rect ${s.w}x${s.h}`
382 * }) // => "circle r=10"
383 * ```
384 *
385 * @see {@link taggedEnum} — creates constructors and matchers
386 *
387 * @category utility types
388 * @since 3.1.0
389 */
390 export type Constructor<A extends { readonly _tag: string }> = Types.Simplify<
391 {
392 readonly [Tag in A["_tag"]]: ConstructorFrom<
393 Extract<A, { readonly _tag: Tag }>,
394 "_tag"
395 >
396 } & {
397 readonly $is: <Tag extends A["_tag"]>(
398 tag: Tag
399 ) => (u: unknown) => u is Extract<A, { readonly _tag: Tag }>
400 readonly $match: {
401 <
402 Cases extends {
403 readonly [Tag in A["_tag"]]: (
404 args: Extract<A, { readonly _tag: Tag }>
405 ) => any
406 }
407 >(
408 cases: Cases
409 ): (value: A) => Unify<ReturnType<Cases[A["_tag"]]>>
410 <
411 Cases extends {
412 readonly [Tag in A["_tag"]]: (
413 args: Extract<A, { readonly _tag: Tag }>
414 ) => any
415 }
416 >(
417 value: A,
418 cases: Cases
419 ): Unify<ReturnType<Cases[A["_tag"]]>>
420 }
421 }
422 >
423
424 /**
425 * Function type that constructs a tagged-union variant from its fields,
426 * excluding the keys listed in `Tag`.
427 *
428 * **When to use**
429 *
430 * Use to type an individual constructor for one tagged-union variant.
431 *
432 * **Details**
433 *
434 * The constructor returns the full variant type `A`. If no fields remain
435 * after excluding `Tag` keys, the constructor argument type becomes `void`.
436 *
437 * @category utility types
438 * @since 4.0.0
439 */
440 export type ConstructorFrom<A, Tag extends keyof A = never> = (
441 args: Types.VoidIfEmpty<{ readonly [P in keyof A as P extends Tag ? never : P]: A[P] }>
442 ) => A
443
444 /**
445 * Type-guard and pattern-matching interface for generic tagged enums.
446 *
447 * **When to use**
448 *
449 * Use to type the `$is` and `$match` helpers for generic tagged enums.
450 *
451 * **Details**
452 *
453 * This is the `$is` / `$match` portion of the object returned by
454 * {@link taggedEnum} when used with a {@link WithGenerics} definition.
455 *
456 * @see {@link Constructor} — the non-generic equivalent
457 *
458 * @category models
459 * @since 3.2.0
460 */
461 export interface GenericMatchers<Z extends WithGenerics<number>> {
462 readonly $is: <Tag extends Z["taggedEnum"]["_tag"]>(
463 tag: Tag
464 ) => {
465 <T extends TaggedEnum.Kind<Z, any, any, any, any>>(
466 u: T
467 ): u is T & { readonly _tag: Tag }
468 (u: unknown): u is Extract<TaggedEnum.Kind<Z>, { readonly _tag: Tag }>
469 }
470 readonly $match: {
471 <
472 A,
473 B,
474 C,
475 D,
476 Cases extends {
477 readonly [Tag in Z["taggedEnum"]["_tag"]]: (
478 args: Extract<
479 TaggedEnum.Kind<Z, A, B, C, D>,
480 { readonly _tag: Tag }
481 >
482 ) => any
483 }
484 >(
485 cases: Cases
486 ): (
487 self: TaggedEnum.Kind<Z, A, B, C, D>
488 ) => Unify<ReturnType<Cases[Z["taggedEnum"]["_tag"]]>>
489 <
490 A,
491 B,
492 C,
493 D,
494 Cases extends {
495 readonly [Tag in Z["taggedEnum"]["_tag"]]: (
496 args: Extract<
497 TaggedEnum.Kind<Z, A, B, C, D>,
498 { readonly _tag: Tag }
499 >
500 ) => any
501 }
502 >(
503 self: TaggedEnum.Kind<Z, A, B, C, D>,
504 cases: Cases
505 ): Unify<ReturnType<Cases[Z["taggedEnum"]["_tag"]]>>
506 }
507 }
508}
509
510/**
511 * Creates constructors and matchers for a `TaggedEnum` type.
512 *
513 * **When to use**
514 *
515 * Use when you model a closed union with plain data objects and want
516 * construction, tag checks, and exhaustive matching from the same definition.
517 *
518 * **Details**
519 *
520 * Returns an object with:
521 * - One constructor per variant (keyed by tag name)
522 * - `$is(tag)` — returns a type-guard function that checks only the `_tag` field
523 * - `$match` — exhaustive pattern matching (data-first or data-last)
524 *
525 * **Gotchas**
526 *
527 * - Constructors produce **plain objects**, not class instances.
528 * - `$is(tag)` only checks the `_tag` field, not the full structure. It relies
529 * on the tag being globally unique and the value being produced by your
530 * constructors. For untrusted input, validate with the `Schema` module first.
531 *
532 * **Example** (Creating and matching tagged enum values)
533 *
534 * ```ts import.meta.vitest
535 * import { Data } from "effect"
536 *
537 * type HttpError = Data.TaggedEnum<{
538 * BadRequest: { readonly message: string }
539 * NotFound: { readonly url: string }
540 * }>
541 *
542 * const { BadRequest, NotFound, $is, $match } = Data.taggedEnum<HttpError>()
543 *
544 * const err = NotFound({ url: "/missing" })
545 *
546 * $is("NotFound")(err) // => true
547 *
548 * $match(err, {
549 * BadRequest: (e) => e.message,
550 * NotFound: (e) => `${e.url} not found`
551 * }) // => "/missing not found"
552 * ```
553 *
554 * **Example** (Defining a generic tagged enum)
555 *
556 * ```ts import.meta.vitest
557 * import { Data } from "effect"
558 *
559 * type MyResult<E, A> = Data.TaggedEnum<{
560 * Failure: { readonly error: E }
561 * Success: { readonly value: A }
562 * }>
563 * interface MyResultDef extends Data.TaggedEnum.WithGenerics<2> {
564 * readonly taggedEnum: MyResult<this["A"], this["B"]>
565 * }
566 * const { Failure, Success } = Data.taggedEnum<MyResultDef>()
567 *
568 * const ok = Success({ value: 42 })
569 * // ok: { readonly _tag: "Success"; readonly value: number }
570 * ok // => { value: 42, _tag: "Success" }
571 * ```
572 *
573 * @see {@link TaggedEnum} — the type-level companion
574 * @see {@link TaggedEnum.Constructor} — the returned object type
575 * @see {@link TaggedEnum.WithGenerics} — generic enum support
576 *
577 * @category constructors
578 * @since 2.0.0
579 */
580export const taggedEnum: {
581 <Z extends TaggedEnum.WithGenerics<1>>(): Types.Simplify<
582 {
583 readonly [Tag in Z["taggedEnum"]["_tag"]]: <A>(
584 args: TaggedEnum.Args<
585 TaggedEnum.Kind<Z, A>,
586 Tag,
587 Extract<TaggedEnum.Kind<Z, A>, { readonly _tag: Tag }>
588 >
589 ) => TaggedEnum.Value<TaggedEnum.Kind<Z, A>, Tag>
590 } & TaggedEnum.GenericMatchers<Z>
591 >
592
593 <Z extends TaggedEnum.WithGenerics<2>>(): Types.Simplify<
594 {
595 readonly [Tag in Z["taggedEnum"]["_tag"]]: <A, B>(
596 args: TaggedEnum.Args<
597 TaggedEnum.Kind<Z, A, B>,
598 Tag,
599 Extract<TaggedEnum.Kind<Z, A, B>, { readonly _tag: Tag }>
600 >
601 ) => TaggedEnum.Value<TaggedEnum.Kind<Z, A, B>, Tag>
602 } & TaggedEnum.GenericMatchers<Z>
603 >
604
605 <Z extends TaggedEnum.WithGenerics<3>>(): Types.Simplify<
606 {
607 readonly [Tag in Z["taggedEnum"]["_tag"]]: <A, B, C>(
608 args: TaggedEnum.Args<
609 TaggedEnum.Kind<Z, A, B, C>,
610 Tag,
611 Extract<TaggedEnum.Kind<Z, A, B, C>, { readonly _tag: Tag }>
612 >
613 ) => TaggedEnum.Value<TaggedEnum.Kind<Z, A, B, C>, Tag>
614 } & TaggedEnum.GenericMatchers<Z>
615 >
616
617 <Z extends TaggedEnum.WithGenerics<4>>(): Types.Simplify<
618 {
619 readonly [Tag in Z["taggedEnum"]["_tag"]]: <A, B, C, D>(
620 args: TaggedEnum.Args<
621 TaggedEnum.Kind<Z, A, B, C, D>,
622 Tag,
623 Extract<TaggedEnum.Kind<Z, A, B, C, D>, { readonly _tag: Tag }>
624 >
625 ) => TaggedEnum.Value<TaggedEnum.Kind<Z, A, B, C, D>, Tag>
626 } & TaggedEnum.GenericMatchers<Z>
627 >
628
629 <A extends { readonly _tag: string }>(): TaggedEnum.Constructor<A>
630} = () =>
631 new Proxy(
632 {},
633 {
634 get(_target, tag, _receiver) {
635 if (tag === "$is") {
636 return Predicate.isTagged
637 } else if (tag === "$match") {
638 return taggedMatch
639 }
640 return (props: any) => ({ ...props, _tag: tag })
641 }
642 }
643 ) as any
644
645function taggedMatch<
646 A extends { readonly _tag: string },
647 Cases extends {
648 readonly [K in A["_tag"]]: (args: Extract<A, { readonly _tag: K }>) => any
649 }
650>(self: A, cases: Cases): ReturnType<Cases[A["_tag"]]>
651function taggedMatch<
652 A extends { readonly _tag: string },
653 Cases extends {
654 readonly [K in A["_tag"]]: (args: Extract<A, { readonly _tag: K }>) => any
655 }
656>(cases: Cases): (value: A) => ReturnType<Cases[A["_tag"]]>
657function taggedMatch<
658 A extends { readonly _tag: string },
659 Cases extends {
660 readonly [K in A["_tag"]]: (args: Extract<A, { readonly _tag: K }>) => any
661 }
662>(): any {
663 if (arguments.length === 1) {
664 const cases = arguments[0] as Cases
665 return function(value: A): ReturnType<Cases[A["_tag"]]> {
666 return cases[value._tag as A["_tag"]](value as any)
667 }
668 }
669 const value = arguments[0] as A
670 const cases = arguments[1] as Cases
671 return cases[value._tag as A["_tag"]](value as any)
672}
673
674/**
675 * Provides a base class for yieldable errors.
676 *
677 * **When to use**
678 *
679 * Use when you need yieldable errors that do **not** need tag-based
680 * discrimination.
681 *
682 * **Details**
683 *
684 * Extends `Cause.YieldableError`, so instances can be yielded inside
685 * `Effect.gen` to fail the enclosing effect. Fields are passed as a single
686 * object; when there are no fields the argument is optional. If a `message`
687 * field is provided, it becomes the error's `.message`.
688 *
689 * **Example** (Defining a yieldable error)
690 *
691 * ```ts import.meta.vitest
692 * import { Data, Effect, Exit } from "effect"
693 *
694 * class NetworkError extends Data.Error<{
695 * readonly code: number
696 * readonly message: string
697 * }> {}
698 *
699 * const program = Effect.gen(function*() {
700 * return yield* new NetworkError({ code: 500, message: "timeout" })
701 * })
702 *
703 * Effect.runSync(Effect.exit(program)) // => Exit.fail(new NetworkError({ code: 500, message: "timeout" }))
704 * ```
705 *
706 * @see {@link TaggedError} — adds a `_tag` for `Effect.catchTag`
707 * @see {@link Class} — non-error data class
708 *
709 * @category constructors
710 * @since 2.0.0
711 */
712export const Error: new<A extends Record<string, any> = {}>(
713 args: Types.VoidIfEmpty<{ readonly [P in keyof A]: A[P] }>
714) => Cause.YieldableError & Readonly<A> = core.Error
715
716/**
717 * Creates a tagged error class with a `_tag` discriminator.
718 *
719 * **When to use**
720 *
721 * Use when you need domain errors with discriminated-union handling.
722 *
723 * **Details**
724 *
725 * Like {@link Error}, but instances also carry a `readonly _tag` property,
726 * enabling `Effect.catchTag` and `Effect.catchTags` for tag-based recovery.
727 * The `_tag` is excluded from the constructor argument. Yielding an instance
728 * inside `Effect.gen` fails the effect with this error.
729 *
730 * **Example** (Recovering by tag)
731 *
732 * ```ts import.meta.vitest
733 * import { Data, Effect } from "effect"
734 *
735 * class NotFound extends Data.TaggedError("NotFound")<{
736 * readonly resource: string
737 * }> {}
738 *
739 * class Forbidden extends Data.TaggedError("Forbidden")<{
740 * readonly reason: string
741 * }> {}
742 *
743 * const program = Effect.gen(function*() {
744 * return yield* new NotFound({ resource: "/users/42" })
745 * })
746 *
747 * const recovered = program.pipe(
748 * Effect.catchTag("NotFound", (e) =>
749 * Effect.succeed(`missing: ${e.resource}`))
750 * )
751 *
752 * await Effect.runPromise(recovered) // => "missing: /users/42"
753 * ```
754 *
755 * @see {@link Error} — without a `_tag`
756 * @see {@link TaggedClass} — tagged class that is not an error
757 *
758 * @category constructors
759 * @since 2.0.0
760 */
761export const TaggedError: <Tag extends string>(
762 tag: Tag
763) => new<A extends Record<string, any> = {}>(
764 args: Types.VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P] }>
765) => Cause.YieldableError & { readonly _tag: Tag } & Readonly<A> = core.TaggedError as any