Hyperlinkv0.8.0-beta.28

Data

Data.TaggedEnumtypeeffect/Data.ts:149
TaggedEnum<A>

Transforms a record of variant definitions into a discriminated union type.

When to use

Use when you have two or more variants that share a common _tag discriminator.

Details

Each key in the record becomes a variant with readonly _tag set to that key. Use with taggedEnum to get constructors and matchers.

Gotchas

Variant records must not include a _tag property; it is added automatically.

Example (Defining a tagged enum)

import { Data } from "effect"

type HttpError = Data.TaggedEnum<{
  BadRequest: { readonly status: 400; readonly message: string }
  NotFound: { readonly status: 404 }
}>

// Equivalent to:
// | { readonly _tag: "BadRequest"; readonly status: 400; readonly message: string }
// | { readonly _tag: "NotFound"; readonly status: 404 }

const { BadRequest, NotFound } = Data.taggedEnum<HttpError>()

const err = BadRequest({ status: 400, message: "missing id" })
console.log(err._tag)
// "BadRequest"
modelstaggedEnumTaggedEnum.WithGenericsTaggedEnum.Constructor
Source effect/Data.ts:149368 lines
export type TaggedEnum<
  A extends Record<string, Record<string, any>> & UntaggedChildren<A>
> = keyof A extends infer Tag ? Tag extends keyof A ? Types.Simplify<
      { readonly _tag: Tag } & { readonly [K in keyof A[Tag]]: A[Tag][K] }
    >
  : never
  : never

type ChildrenAreTagged<A> = keyof A extends infer K ? K extends keyof A ? "_tag" extends keyof A[K] ? true
    : false
  : never
  : never

type UntaggedChildren<A> = true extends ChildrenAreTagged<A>
  ? "It looks like you're trying to create a tagged enum, but one or more of its members already has a `_tag` property."
  : unknown

/**
 * Namespace for `TaggedEnum` utility types.
 *
 * **When to use**
 *
 * Use to reference utility types for constructing, extracting, and matching
 * `TaggedEnum` variants.
 *
 * **Details**
 *
 * Provides helper types for:
 * - Generic tagged enums ({@link TaggedEnum.WithGenerics}, {@link TaggedEnum.Kind})
 * - Extracting constructor arguments ({@link TaggedEnum.Args}) and variant
 *   values ({@link TaggedEnum.Value})
 * - Full constructor objects ({@link TaggedEnum.Constructor})
 *
 * @since 2.0.0
 */
export declare namespace TaggedEnum {
  /**
   * Defines a tagged enum shape that accepts generic type parameters.
   *
   * **When to use**
   *
   * Use when variant payloads need to be parameterized, such as `Result<E, A>`.
   *
   * **Details**
   *
   * Extend this interface and set `taggedEnum` to your union type, using
   * `this["A"]`, `this["B"]`, etc. as placeholders for the generics. The
   * `Count` parameter declares how many generics are used (up to 4).
   *
   * **Example** (Defining a generic tagged enum)
   *
   * ```ts
   * import { Data } from "effect"
   *
   * type MyResult<E, A> = Data.TaggedEnum<{
   *   Failure: { readonly error: E }
   *   Success: { readonly value: A }
   * }>
   *
   * interface MyResultDef extends Data.TaggedEnum.WithGenerics<2> {
   *   readonly taggedEnum: MyResult<this["A"], this["B"]>
   * }
   *
   * const { Failure, Success } = Data.taggedEnum<MyResultDef>()
   *
   * const ok = Success({ value: 42 })
   * // ok: { readonly _tag: "Success"; readonly value: number }
   * ```
   *
   * @see {@link Kind} — apply concrete types to a `WithGenerics` definition
   * @see {@link taggedEnum} — constructors and matchers
   *
   * @category models
   * @since 2.0.0
   */
  export interface WithGenerics<Count extends number> {
    readonly taggedEnum: { readonly _tag: string }
    readonly numberOfGenerics: Count

    readonly A: unknown
    readonly B: unknown
    readonly C: unknown
    readonly D: unknown
  }

  /**
   * Applies concrete type arguments to a `WithGenerics` definition, producing
   * the resulting tagged union type.
   *
   * **When to use**
   *
   * Use to refer to a specific instantiation of a generic tagged enum in type signatures.
   *
   * **Example** (Applying generics)
   *
   * ```ts
   * import type { Data } from "effect"
   *
   * type Option<A> = Data.TaggedEnum<{
   *   None: {}
   *   Some: { readonly value: A }
   * }>
   * interface OptionDef extends Data.TaggedEnum.WithGenerics<1> {
   *   readonly taggedEnum: Option<this["A"]>
   * }
   *
   * // Resolve to the concrete union for `string`
   * type StringOption = Data.TaggedEnum.Kind<OptionDef, string>
   * // { _tag: "None" } | { _tag: "Some"; value: string }
   * ```
   *
   * @see {@link WithGenerics} — define the generic shape
   *
   * @category utility types
   * @since 2.0.0
   */
  export type Kind<
    Z extends WithGenerics<number>,
    A = unknown,
    B = unknown,
    C = unknown,
    D = unknown
  > = (Z & {
    readonly A: A
    readonly B: B
    readonly C: C
    readonly D: D
  })["taggedEnum"]

  /**
   * Extracts the constructor argument type for a specific variant of a tagged
   * union.
   *
   * **When to use**
   *
   * Use to derive the argument object expected by a constructor for one tagged
   * union variant.
   *
   * **Details**
   *
   * Returns `void` if the variant has no fields beyond `_tag`.
   *
   * **Example** (Extracting variant args)
   *
   * ```ts
   * import type { Data } from "effect"
   *
   * type Result =
   *   | { readonly _tag: "Ok"; readonly value: number }
   *   | { readonly _tag: "Err"; readonly error: string }
   *
   * type OkArgs = Data.TaggedEnum.Args<Result, "Ok">
   * // { readonly value: number }
   *
   * type ErrArgs = Data.TaggedEnum.Args<Result, "Err">
   * // { readonly error: string }
   * ```
   *
   * @see {@link Value} — extracts the full variant type (including `_tag`)
   *
   * @category utility types
   * @since 2.0.0
   */
  export type Args<
    A extends { readonly _tag: string },
    K extends A["_tag"],
    E = Extract<A, { readonly _tag: K }>
  > = {
    readonly [K in keyof E as K extends "_tag" ? never : K]: E[K]
  } extends infer T ? Types.VoidIfEmpty<T>
    : never

  /**
   * Extracts the full variant type (including `_tag`) for a specific tag.
   *
   * **When to use**
   *
   * Use to select one full tagged-union variant by its `_tag` value.
   *
   * **Example** (Extracting a variant type)
   *
   * ```ts
   * import type { Data } from "effect"
   *
   * type Result =
   *   | { readonly _tag: "Ok"; readonly value: number }
   *   | { readonly _tag: "Err"; readonly error: string }
   *
   * type OkVariant = Data.TaggedEnum.Value<Result, "Ok">
   * // { readonly _tag: "Ok"; readonly value: number }
   * ```
   *
   * @see {@link Args} — extracts fields without `_tag`
   *
   * @category utility types
   * @since 2.0.0
   */
  export type Value<
    A extends { readonly _tag: string },
    K extends A["_tag"]
  > = Extract<A, { readonly _tag: K }>

  /**
   * The full constructors-and-matchers object type returned by {@link taggedEnum}.
   *
   * **When to use**
   *
   * Use when you want to annotate an exported constructor bundle so downstream
   * code keeps exact variant constructors and exhaustive matching.
   *
   * **Details**
   *
   * Includes:
   * - A constructor function for each variant (keyed by tag name)
   * - `$is(tag)` — returns a type-guard that checks only the `_tag` field;
   *   safe when the tag is globally unique and the value was produced by your
   *   constructors. For untrusted input, validate with the `Schema` module first.
   * - `$match` — exhaustive pattern matching (data-last or data-first)
   *
   * **Example** (Using the constructor object)
   *
   * ```ts
   * import { Data } from "effect"
   *
   * type Shape =
   *   | { readonly _tag: "Circle"; readonly radius: number }
   *   | { readonly _tag: "Rect"; readonly w: number; readonly h: number }
   *
   * const { Circle, Rect, $is, $match } = Data.taggedEnum<Shape>()
   *
   * const shape = Circle({ radius: 10 })
   *
   * // Type guard
   * if ($is("Circle")(shape)) {
   *   console.log(shape.radius)
   * }
   *
   * // Pattern matching
   * const label = $match(shape, {
   *   Circle: (s) => `circle r=${s.radius}`,
   *   Rect: (s) => `rect ${s.w}x${s.h}`
   * })
   * ```
   *
   * @see {@link taggedEnum} — creates constructors and matchers
   *
   * @category types
   * @since 3.1.0
   */
  export type Constructor<A extends { readonly _tag: string }> = Types.Simplify<
    {
      readonly [Tag in A["_tag"]]: ConstructorFrom<
        Extract<A, { readonly _tag: Tag }>,
        "_tag"
      >
    } & {
      readonly $is: <Tag extends A["_tag"]>(
        tag: Tag
      ) => (u: unknown) => u is Extract<A, { readonly _tag: Tag }>
      readonly $match: {
        <
          Cases extends {
            readonly [Tag in A["_tag"]]: (
              args: Extract<A, { readonly _tag: Tag }>
            ) => any
          }
        >(
          cases: Cases
        ): (value: A) => Unify<ReturnType<Cases[A["_tag"]]>>
        <
          Cases extends {
            readonly [Tag in A["_tag"]]: (
              args: Extract<A, { readonly _tag: Tag }>
            ) => any
          }
        >(
          value: A,
          cases: Cases
        ): Unify<ReturnType<Cases[A["_tag"]]>>
      }
    }
  >

  /**
   * Function type that constructs a tagged-union variant from its fields,
   * excluding the keys listed in `Tag`.
   *
   * **When to use**
   *
   * Use to type an individual constructor for one tagged-union variant.
   *
   * **Details**
   *
   * The constructor returns the full variant type `A`. If no fields remain
   * after excluding `Tag` keys, the constructor argument type becomes `void`.
   *
   * @category utility types
   * @since 4.0.0
   */
  export type ConstructorFrom<A, Tag extends keyof A = never> = (
    args: Types.VoidIfEmpty<{ readonly [P in keyof A as P extends Tag ? never : P]: A[P] }>
  ) => A

  /**
   * Type-guard and pattern-matching interface for generic tagged enums.
   *
   * **When to use**
   *
   * Use to type the `$is` and `$match` helpers for generic tagged enums.
   *
   * **Details**
   *
   * This is the `$is` / `$match` portion of the object returned by
   * {@link taggedEnum} when used with a {@link WithGenerics} definition.
   *
   * @see {@link Constructor} — the non-generic equivalent
   *
   * @category models
   * @since 3.2.0
   */
  export interface GenericMatchers<Z extends WithGenerics<number>> {
    readonly $is: <Tag extends Z["taggedEnum"]["_tag"]>(
      tag: Tag
    ) => {
      <T extends TaggedEnum.Kind<Z, any, any, any, any>>(
        u: T
      ): u is T & { readonly _tag: Tag }
      (u: unknown): u is Extract<TaggedEnum.Kind<Z>, { readonly _tag: Tag }>
    }
    readonly $match: {
      <
        A,
        B,
        C,
        D,
        Cases extends {
          readonly [Tag in Z["taggedEnum"]["_tag"]]: (
            args: Extract<
              TaggedEnum.Kind<Z, A, B, C, D>,
              { readonly _tag: Tag }
            >
          ) => any
        }
      >(
        cases: Cases
      ): (
        self: TaggedEnum.Kind<Z, A, B, C, D>
      ) => Unify<ReturnType<Cases[Z["taggedEnum"]["_tag"]]>>
      <
        A,
        B,
        C,
        D,
        Cases extends {
          readonly [Tag in Z["taggedEnum"]["_tag"]]: (
            args: Extract<
              TaggedEnum.Kind<Z, A, B, C, D>,
              { readonly _tag: Tag }
            >
          ) => any
        }
      >(
        self: TaggedEnum.Kind<Z, A, B, C, D>,
        cases: Cases
      ): Unify<ReturnType<Cases[Z["taggedEnum"]["_tag"]]>>
    }
  }
}
Referenced by 1 symbols