# Data

Plain JavaScript objects compare by *reference*: two objects with identical
contents are not `===`, and they hash differently in a `Set`. The `Data` module
fixes this. It provides base classes whose instances compare by *value* via
[`Equal.equals`](https://effect.plants.sh/traits/equal-and-hash/), which makes them safe to compare in
tests and to use as keys in `HashMap`/`HashSet`. `Data` also gives you ergonomic
discriminated unions through `TaggedEnum`.

```ts
import { Data, Equal } from "effect"

// A value class: fields are declared via the type parameter,
// and passed to the constructor as a single object
class Point extends Data.Class<{
  readonly x: number
  readonly y: number
}> {}

const a = new Point({ x: 1, y: 2 })
const b = new Point({ x: 1, y: 2 })

// Reference equality would say false; Data gives structural equality
console.log(a === b)             // false
console.log(Equal.equals(a, b))  // true
```

`Data.Class` builds an immutable value type. Instances are `Readonly`, support
`.pipe()`, and — crucially — are equal when their fields are equal.

## Tagged structs

`Data.TaggedClass` adds a `readonly _tag` discriminator, which you can match on.
This is the building block for modelling one variant of a domain type.

```ts
import { Data } from "effect"

class User extends Data.TaggedClass("User")<{
  readonly id: number
  readonly name: string
}> {}

const user = new User({ id: 1, name: "Mike" })

console.log(user._tag) // "User"
console.log(user.name) // "Mike"
```

The `_tag` is set for you — you do not pass it to the constructor.

## Tagged unions

For a type with several variants, declare a `Data.TaggedEnum` and generate its
constructors and helpers with `Data.taggedEnum`. Each variant becomes a
constructor, and you also get `$is` (a type guard) and `$match` (exhaustive
pattern matching).

```ts
import { Data } from "effect"

// A discriminated union of remote-data states
type RemoteData = Data.TaggedEnum<{
  Loading: {}
  Success: { readonly data: string }
  Failure: { readonly error: string }
}>

// Generate constructors + helpers for the union
const { Loading, Success, Failure, $match } = Data.taggedEnum<RemoteData>()

const render = $match({
  Loading: () => "loading…",
  Success: ({ data }) => `loaded: ${data}`,
  Failure: ({ error }) => `error: ${error}`
})

console.log(render(Loading()))                       // "loading…"
console.log(render(Success({ data: "hello" })))      // "loaded: hello"
console.log(render(Failure({ error: "timeout" })))   // "error: timeout"
```

`$match` is exhaustive: omit a case and the code will not compile, so adding a
new variant forces you to handle it everywhere. Use `$is("Success")` when you
just need a type guard for one variant. Because the variants are `Data` values,
they also compare structurally:

```ts
import { Data, Equal } from "effect"

type RemoteData = Data.TaggedEnum<{
  Success: { readonly data: string }
}>
const { Success } = Data.taggedEnum<RemoteData>()

console.log(Equal.equals(Success({ data: "x" }), Success({ data: "x" }))) // true
```

:::caution[`$is` only checks `_tag`]
`taggedEnum` constructors produce **plain objects**, not class instances, and
`$is(tag)` only inspects the `_tag` field — it does not validate the rest of the
structure. It is safe when the tag is globally unique and the value came from
your own constructors. For untrusted input (parsed JSON, request bodies),
validate with the [Schema](https://effect.plants.sh/schema/) module before relying on `$is`.
:::

## Tagged errors

`Data.TaggedError` is the same idea applied to errors: it produces a tagged class
that is also *yieldable* in `Effect.gen`, so you can `yield*` an instance to fail
an effect. It pairs naturally with
[Error Management](https://effect.plants.sh/error-management/tagged-errors/), where `Effect.catchTag`
dispatches on the `_tag`.

```ts
import { Data, Effect } from "effect"

class NotFound extends Data.TaggedError("NotFound")<{
  readonly id: number
}> {}

const find = Effect.fn("find")(function* (id: number) {
  if (id < 0) {
    // Yielding the error fails the effect with NotFound
    return yield* new NotFound({ id })
  }
  return `record ${id}`
})

const program = find(-1).pipe(
  // catchTag narrows on `_tag` and gives you the typed fields
  Effect.catchTag("NotFound", (e) => Effect.succeed(`missing #${e.id}`))
)

Effect.runPromise(program).then(console.log) // "missing #-1"
```

### `Data.TaggedError` vs `Schema.TaggedErrorClass`

Both define tagged, yieldable errors. Choose based on whether the error needs to
cross a process boundary:

- **`Data.TaggedError`** — lightweight, value-equality errors for in-process
  logic. No schema, no (de)serialization. Best when the error never leaves the
  current runtime.
- **`Schema.TaggedErrorClass`** (from [Schema](https://effect.plants.sh/schema/defining-errors/)) — adds
  an encoded representation so the error can be serialized and reconstructed
  across [RPC](https://effect.plants.sh/rpc/) or [HTTP API](https://effect.plants.sh/http-api/) boundaries. Prefer it whenever an
  error is part of a wire contract.

## Migrating from v3

Effect v4's `Data` module is smaller than v3's. The `Data.struct`, `Data.tuple`,
`Data.array`, and `Data.case` helpers have been **removed**. Replace them as
follows:

- For value-equality records, define a `Data.Class` (or `Data.TaggedClass`)
  instead of `Data.struct`.
- For ad-hoc collections that need structural equality, construct plain
  arrays/objects and compare them with [`Equal.equals`](https://effect.plants.sh/traits/equal-and-hash/),
  which already performs structural comparison.
- `Data.case` / `Data.tagged` constructors are subsumed by `Data.taggedEnum`,
  which generates per-variant constructors for you.

## Generic tagged enums

When a variant's payload is parameterized (e.g. a `Result<E, A>`), a plain type
alias cannot carry the generics through `taggedEnum`. Instead, extend
`Data.TaggedEnum.WithGenerics<N>` with an **interface** and pass that interface
to `taggedEnum`. The interface uses `this["A"]`, `this["B"]`, … as placeholders
for up to four generics.

```ts
import { Data } from "effect"

type MyResult<E, A> = Data.TaggedEnum<{
  Failure: { readonly error: E }
  Success: { readonly value: A }
}>

// Pass the *interface* (not the alias) to keep the generics
interface MyResultDef extends Data.TaggedEnum.WithGenerics<2> {
  readonly taggedEnum: MyResult<this["A"], this["B"]>
}

const { Failure, Success, $match } = Data.taggedEnum<MyResultDef>()

const ok = Success({ value: 42 })
// => ok: { readonly _tag: "Success"; readonly value: number }

const describe = $match(ok, {
  Failure: (f) => `error: ${f.error}`,
  Success: (s) => `value: ${s.value}`
})
console.log(describe) // => "value: 42"
```

### `Data.TaggedEnum.WithGenerics`

The interface you extend to declare a generic tagged enum. Set `taggedEnum` to
your union (using `this["A"]`…`this["D"]` for the type parameters); the numeric
type argument is the generic count (1–4). See the example above.

### `Data.TaggedEnum.Kind`

Resolves a `WithGenerics` definition to its concrete tagged union for given type
arguments. Useful when referring to a specific instantiation in a signature.

```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"]>
}

type StringOption = Data.TaggedEnum.Kind<OptionDef, string>
// => { readonly _tag: "None" } | { readonly _tag: "Some"; readonly value: string }
```

### `Data.TaggedEnum.Args`

Extracts the *constructor argument* type for one variant — the variant's fields
without `_tag`. Resolves to `void` when the variant has no extra fields.

```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 }
```

### `Data.TaggedEnum.Value`

Extracts the *full* variant type (including `_tag`) for a given tag.

```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 }
```

### `Data.TaggedEnum.Constructor`

The type of the whole object returned by `taggedEnum` for a non-generic enum: one
`ConstructorFrom` per variant plus the `$is` guard and `$match` matcher. Use it
to annotate a value holding the constructors.

```ts
import type { Data } from "effect"

type Shape =
  | { readonly _tag: "Circle"; readonly radius: number }
  | { readonly _tag: "Rect"; readonly w: number; readonly h: number }

type ShapeCtors = Data.TaggedEnum.Constructor<Shape>
// => { Circle: (args) => ...; Rect: (args) => ...; $is: ...; $match: ... }
```

### `Data.TaggedEnum.ConstructorFrom`

The function type of a single variant constructor: it takes the variant's fields
(excluding the keys named in `Tag`) and returns the full variant. The argument
type becomes `void` when no fields remain.

```ts
import type { Data } from "effect"

type Ok = { readonly _tag: "Ok"; readonly value: number }

type MakeOk = Data.TaggedEnum.ConstructorFrom<Ok, "_tag">
// => (args: { readonly value: number }) => Ok
```

### `Data.TaggedEnum.GenericMatchers`

The `$is` / `$match` portion of the object returned by `taggedEnum` when used
with a `WithGenerics` definition. You rarely reference it directly — it is the
generic-aware counterpart to the matchers inside `Constructor`.

```ts
import type { Data } from "effect"

interface OptionDef extends Data.TaggedEnum.WithGenerics<1> {
  readonly taggedEnum: Data.TaggedEnum<{
    None: {}
    Some: { readonly value: this["A"] }
  }>
}

type Matchers = Data.TaggedEnum.GenericMatchers<OptionDef>
// => { readonly $is: ...; readonly $match: ... }
```

## API reference

The entire v4 `Data` surface: the value-class constructors (`Class`,
`TaggedClass`, `Error`, `TaggedError`), the `TaggedEnum` type plus its
`taggedEnum` constructor factory, and the `TaggedEnum` namespace of utility
types.

### `Data.Class`

Base class for plain immutable data. Extend it with a type parameter that
declares the fields; the constructor takes those fields as a single object (the
argument is optional when there are no fields). Instances are `Readonly`,
`Pipeable`, and compare by value.

```ts
import { Data, Equal } from "effect"

class Person extends Data.Class<{ readonly name: string }> {}

const mike1 = new Person({ name: "Mike" })
const mike2 = new Person({ name: "Mike" })

console.log(Equal.equals(mike1, mike2)) // => true

// No-fields case: the constructor argument is optional
class Anonymous extends Data.Class {}
const anon = new Anonymous()
console.log(anon instanceof Anonymous) // => true
```

### `Data.TaggedClass`

Like `Data.Class`, but the instances also carry a `readonly _tag` literal you
supply at definition time. The `_tag` is set automatically and is **excluded**
from the constructor argument. Use it for a single tagged variant or an ad-hoc
discriminator; for multi-variant unions reach for `taggedEnum`.

```ts
import { Data } from "effect"

class Person extends Data.TaggedClass("Person")<{
  readonly name: string
}> {}

const mike = new Person({ name: "Mike" })
console.log(mike._tag) // => "Person"
console.log(mike.name) // => "Mike"

// No-fields tagged class: constructor argument is optional
class Ping extends Data.TaggedClass("Ping") {}
console.log(new Ping()._tag) // => "Ping"
```

### `Data.TaggedEnum` (type)

A *type-level* helper that turns a record of variant definitions into a
discriminated union, adding `readonly _tag` to each variant from its record key.
Variant records must **not** already contain a `_tag` key. Pair it with
`taggedEnum` to obtain runtime constructors and matchers.

```ts
import type { Data } from "effect"

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

### `Data.taggedEnum`

Creates the runtime constructors and matchers for a `TaggedEnum`. The returned
object has one constructor per variant (keyed by tag), plus `$is` and `$match`.
Constructors produce **plain objects** (not class instances) with `_tag` filled
in. For no-field variants the constructor argument is omitted.

```ts
import { Data } from "effect"

type HttpError = Data.TaggedEnum<{
  BadRequest: { readonly message: string }
  NotFound: { readonly url: string }
}>

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

const err = NotFound({ url: "/missing" })
console.log(err) // => { url: "/missing", _tag: "NotFound" }
```

#### `$is(tag)` — type guard

`$is(tag)` returns a type guard that narrows a value to a single variant. It
checks **only** the `_tag` field, so it is safe for trusted values produced by
your constructors; validate untrusted input with Schema first.

```ts
import { Data } from "effect"

type HttpError = Data.TaggedEnum<{
  BadRequest: { readonly message: string }
  NotFound: { readonly url: string }
}>
const { NotFound, $is } = Data.taggedEnum<HttpError>()

const err = NotFound({ url: "/missing" })
console.log($is("NotFound")(err)) // => true
console.log($is("BadRequest")(err)) // => false
```

#### `$match` — exhaustive matching

`$match` dispatches on `_tag`. It is overloaded: pass `(value, cases)` for
data-first, or `(cases)` for a data-last matcher you can reuse and `.pipe()`
into. Missing a case is a compile error.

```ts
import { Data } from "effect"

type HttpError = Data.TaggedEnum<{
  BadRequest: { readonly message: string }
  NotFound: { readonly url: string }
}>
const { BadRequest, NotFound, $match } = Data.taggedEnum<HttpError>()

// data-first
console.log(
  $match(NotFound({ url: "/x" }), {
    BadRequest: (e) => e.message,
    NotFound: (e) => `${e.url} not found`
  })
) // => "/x not found"

// data-last (reusable matcher)
const toMessage = $match({
  BadRequest: (e) => e.message,
  NotFound: (e) => `${e.url} not found`
})
console.log(toMessage(BadRequest({ message: "bad" }))) // => "bad"
```

### `Data.Error`

Base class for yieldable errors **without** a tag. Extends
`Cause.YieldableError`, so an instance can be `yield*`-ed inside `Effect.gen` to
fail the surrounding effect. Fields are passed as one object; a `message` field
becomes the error's `.message`.

```ts
import { Data, Effect } from "effect"

class NetworkError extends Data.Error<{
  readonly code: number
  readonly message: string
}> {}

const program = Effect.gen(function* () {
  return yield* new NetworkError({ code: 500, message: "timeout" })
})

Effect.runPromise(Effect.flip(program)).then((e) =>
  console.log(e.code, e.message)
) // => 500 "timeout"
```

### `Data.TaggedError`

Like `Data.Error`, but instances also carry a `readonly _tag`, enabling
`Effect.catchTag` / `Effect.catchTags` for tag-based recovery. The `_tag` is
excluded from the constructor argument. This is the recommended way to model
in-process domain errors.

```ts
import { Data, Effect } from "effect"

class NotFound extends Data.TaggedError("NotFound")<{
  readonly resource: string
}> {}

const program = Effect.gen(function* () {
  return yield* new NotFound({ resource: "/users/42" })
})

const recovered = program.pipe(
  Effect.catchTag("NotFound", (e) => Effect.succeed(`missing: ${e.resource}`))
)

Effect.runPromise(recovered).then(console.log) // => "missing: /users/42"
```

## See also

- [Equal & Hash](https://effect.plants.sh/traits/equal-and-hash/) — the value-equality trait that all
  `Data` instances implement.
- [Pattern Matching](https://effect.plants.sh/pattern-matching/) — the standalone `Match` module, for
  matching beyond `$match`.
- [Tagged errors](https://effect.plants.sh/error-management/tagged-errors/) — recovering from
  `Data.TaggedError` with `catchTag` / `catchTags`.
- [Defining errors with Schema](https://effect.plants.sh/schema/defining-errors/) — serializable errors
  for RPC/HTTP boundaries.