# Branded Types

TypeScript is structurally typed: two types with the same shape are
interchangeable. A `UserId` that is just `number` and an `OrderId` that is also
`number` can be passed to each other's functions with no complaint from the
compiler — exactly the kind of mix-up that causes real bugs. **Branded types**
attach a compile-time tag to a value so the type system keeps these apart, while
leaving the runtime representation untouched.

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

// `Branded<base, key>` = the base type plus a phantom brand. At runtime a
// UserId is still a number; the brand only exists in the type system.
type UserId = Brand.Branded<number, "UserId">
type OrderId = Brand.Branded<number, "OrderId">

// `nominal` makes a constructor with no runtime check — it only applies the tag.
const UserId = Brand.nominal<UserId>()
const OrderId = Brand.nominal<OrderId>()

const getUser = (id: UserId) => `loading user ${id}`

getUser(UserId(1)) // ✅ ok
// @ts-expect-error — an OrderId is not a UserId, even though both are numbers
getUser(OrderId(1))
// @ts-expect-error — a raw number is missing the brand
getUser(1)
```

`UserId(1)` returns the very same `1` at runtime; the difference lives entirely
in the types, so branding costs nothing at execution time.

## When to use branding

Reach for a branded type when a primitive carries meaning that the compiler
should enforce:

- **Distinct identifiers** that share a representation — `UserId` vs `OrderId`,
  both `string` or both `number`.
- **Validated primitives** — a `number` that has been checked to be a positive
  integer, a `string` that is a non-empty email, a token that has been
  normalized. The brand records that the validation happened, so downstream code
  can trust it without re-checking.

If a value never gets confused with another and needs no validation guarantee,
plain types are fine — don't brand reflexively.

## Nominal vs validated constructors

The `Brand` module gives you two kinds of constructor.

`Brand.nominal` only applies the tag. Use it for identifiers where any value of
the base type is already valid and you just want them kept distinct.

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

type Sku = Brand.Branded<string, "Sku">
const Sku = Brand.nominal<Sku>()

const sku = Sku("ABC-123") // always succeeds
```

  `Brand.make` builds a constructor from a validation predicate. The predicate
returns `true` (or `undefined`) when the value is valid, or a **message string**
describing why it failed.

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

type PositiveInt = Brand.Branded<number, "PositiveInt">

const PositiveInt = Brand.make<PositiveInt>(
  (n) => (Number.isInteger(n) && n > 0) || "Expected a positive integer"
)

const good = PositiveInt(3) // ✅ branded value
// PositiveInt(-1) would throw a BrandError at runtime
```

  A validated constructor throws a `BrandError` when called with an invalid value.
That is convenient at trust boundaries, but you usually want a non-throwing form.

## Safe construction: `option`, `result`, `is`

Every constructor — nominal or validated — exposes methods that validate without
throwing, so you can integrate branding into Effect flows cleanly.

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

type PositiveInt = Brand.Branded<number, "PositiveInt">

const PositiveInt = Brand.make<PositiveInt>(
  (n) => (Number.isInteger(n) && n > 0) || "Expected a positive integer"
)

// `.option` → Option<PositiveInt>
const maybe = PositiveInt.option(5)

// `.result` → Result<PositiveInt, BrandError>, ideal inside Effect.gen
const parseQuantity = Effect.fn("parseQuantity")(function*(raw: number) {
  const result = PositiveInt.result(raw)
  if (result._tag === "Failure") {
    return yield* Effect.fail(`invalid quantity: ${result.failure.message}`)
  }
  return result.success
})

// `.is` → a type guard you can use to narrow
declare const value: number
if (PositiveInt.is(value)) {
  // value is now PositiveInt here
}
```
**Note:** `BrandError` is an error-like value (it has `_tag`, `name`, and `message`) but it
does **not** extend JavaScript's `Error`. Read its `message` for the
human-readable reason. See [Data Types](https://effect.plants.sh/data-types/) for the `Result` and
`Option` types these methods return.

## Combining brands with `Brand.all`

When a value must satisfy several independent constraints, define each brand
separately and merge their constructors with `Brand.all`. The combined
constructor enforces every check, and the resulting type carries every tag.

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

type Int = Brand.Branded<number, "Int">
const Int = Brand.make<Int>(
  (n) => Number.isInteger(n) || "Expected an integer"
)

type Positive = Brand.Branded<number, "Positive">
const Positive = Brand.make<Positive>(
  (n) => n > 0 || "Expected a positive number"
)

// PositiveInt requires BOTH checks to pass.
const PositiveInt = Brand.all(Int, Positive)
type PositiveInt = Brand.Brand.FromConstructor<typeof PositiveInt>

PositiveInt(10) // ✅
// PositiveInt(-5) → BrandError("Expected a positive number")
// PositiveInt(3.14) → BrandError("Expected an integer")
```

All brands passed to `Brand.all` must share the same base type (here, `number`).

## Branding through Schema

When the value flows through a [Schema](https://effect.plants.sh/schema/) — parsing config, decoding a
request body, validating a database row — brand it at the schema level so decoded
values come out already tagged. `Schema.brand` adds the nominal tag, while
`Schema.fromBrand` also applies a validating constructor's checks during
decoding.

```ts
import { Brand, Schema } from "effect"

// Tag a schema's output. `Schema.Int` already validates integer-ness;
// `.pipe(Schema.brand(...))` makes the result a distinct branded type.
const UserId = Schema.Int.pipe(Schema.brand("UserId"))
type UserId = typeof UserId.Type // number & Brand<"UserId">

// Or reuse an existing Brand constructor so its checks run during decoding.
type Slug = Brand.Branded<string, "Slug">
const Slug = Brand.make<Slug>(
  (s) => /^[a-z0-9-]+$/.test(s) || "Expected a url-safe slug"
)
const SlugSchema = Schema.String.pipe(Schema.fromBrand("Slug", Slug))
```

This keeps validation and branding in one place: every value that exits the
schema is guaranteed to satisfy the brand's invariants, and the type system
propagates that guarantee through the rest of your program.