# Branded Types

TypeScript is *structurally* typed: a `UserId` that is "just a number" is
interchangeable with any other number, so nothing stops you from passing an
`OrderId` — or a raw `42` — where a `UserId` is expected. **Branded types** add a
phantom tag to a value so the compiler treats it as a distinct, *nominal* type
while it remains an ordinary number (or string) at runtime. The `Brand` module
gives you the tag and a constructor that produces branded values.

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

// A branded number type: still a number at runtime, distinct at compile time
type UserId = number & Brand.Brand<"UserId">

// `nominal` builds a constructor that applies no runtime validation
const UserId = Brand.nominal<UserId>()

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

getUser(UserId(1)) // ok
// getUser(1)       // compile error: number is not a UserId
```

`Brand.Brand<"UserId">` is the phantom tag, and `Brand.nominal<UserId>()`
returns a constructor that simply re-labels a plain number as a `UserId`. The
mislabeled call `getUser(1)` no longer type-checks, so the two "kinds of number"
can never be confused.

For *when* and *why* to reach for brands (the conceptual guide and modeling
trade-offs), see [Branded types in the code-style guide](https://effect.plants.sh/code-style/branded-types/).
This page is the exhaustive `Brand` API reference.

## Nominal brands (no validation)

Use `Brand.nominal` when you only want to *distinguish* values, with no runtime
check. The constructor is the identity at runtime:

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

type Email = string & Brand.Brand<"Email">
type Username = string & Brand.Brand<"Username">

const Email = Brand.nominal<Email>()
const Username = Brand.nominal<Username>()

const email = Email("john@example.com")
const name = Username("john")

// Email and Username are now incompatible even though both are strings
```

## Validated brands

Often a brand carries an *invariant*: a `UserId` must be a positive integer, an
`Email` must contain an `@`. `Brand.make` builds a constructor that validates
its input. The filter returns `true` when the value is valid, or an error
message string when it is not.

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

type Int = number & Brand.Brand<"Int">

// The filter returns true (valid) or an error message (invalid)
const Int = Brand.make<Int>((n) =>
  Number.isInteger(n) || `Expected ${n} to be an integer`
)

console.log(Int(42)) // 42

// Int(1.5) // throws BrandError: "Expected 1.5 to be an integer"
```

By default, calling the constructor throws a `BrandError` on invalid input. To
handle failure as a value instead, the constructor also exposes total variants:

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

type Int = number & Brand.Brand<"Int">
const Int = Brand.make<Int>((n) =>
  Number.isInteger(n) || `Expected ${n} to be an integer`
)

// `.option` returns Option<Int>
console.log(Int.option(42))  // { _id: 'Option', _tag: 'Some', value: 42 }
console.log(Int.option(1.5)) // { _id: 'Option', _tag: 'None' }

// `.result` returns Result<Int, BrandError>
console.log(Int.result(1.5))
// { _id: 'Result', _tag: 'Failure', failure: BrandError(...) }

// `.is` is a type guard
console.log(Int.is(42))  // true
console.log(Int.is(1.5)) // false
```

So every brand constructor gives you four ways to construct, depending on how
you want to treat invalid input: `Int(x)` (throws), `Int.option(x)`,
`Int.result(x)`, and `Int.is(x)`.

## Validating with schema checks

When your validation logic already exists as [Schema](https://effect.plants.sh/schema/) checks, use
`Brand.check` to reuse them. Multiple checks accumulate, and the failure message
comes from the schema:

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

type PositiveInt = number & Brand.Brand<"PositiveInt">

// Combine schema checks into a branded constructor
const PositiveInt = Brand.check<PositiveInt>(
  Schema.isInt(),
  Schema.isGreaterThan(0)
)

console.log(PositiveInt(5)) // 5
console.log(PositiveInt.option(-1)) // None — fails the "> 0" check
```

## Brands and Schema

If a value enters your system through a [Schema](https://effect.plants.sh/schema/) (decoding JSON from an
HTTP request, say), you usually want the brand applied as part of decoding rather
than as a separate step. The Schema module's `.pipe(Schema.brand("..."))`
attaches a brand to a decoded value, so the parsed result is already a
branded type with its invariants checked. Reach for the standalone `Brand`
module when you are branding values that do not flow through a schema.

## Brand reference

Everything `Brand` exports. The two spellings for a branded value are
**equivalent** — pick whichever reads better:

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

// Intersection spelling (used above)
type A = number & Brand.Brand<"Score">

// Alias spelling — Branded<A, Key> is exactly A & Brand<Key>
type B = Brand.Branded<number, "Score">
```

### Brand.Brand\<Keys>

The phantom tag interface. It contributes no runtime fields (the brand key lives
in a unique symbol property), and it can carry **multiple** keys at once so a
single value may hold several brands.

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

// One key
type UserId = number & Brand.Brand<"UserId">

// Several keys on one value (e.g. produced by Brand.all below)
type PositiveInt = number & Brand.Brand<"Positive"> & Brand.Brand<"Int">

const x = 1 as PositiveInt
const useId = (n: UserId) => n
// useId(x) // compile error: PositiveInt is not a UserId
```

### Brand.Branded\<A, Key>

A type alias for `A & Brand<Key>`, slightly more concise when you have a single
brand key.

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

type Slug = Brand.Branded<string, "Slug"> // ≡ string & Brand.Brand<"Slug">

const Slug = Brand.nominal<Slug>()
const s = Slug("hello-world") // => "hello-world" (typed as Slug)
```

### Brand.nominal

Builds a `Constructor` that applies **no** runtime validation and returns its
input unchanged — purely for distinguishing values of the same base type.

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

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

console.log(OrderId("ord_42"))        // => "ord_42"
console.log(OrderId.option("ord_42")) // => Some("ord_42")  (always Some)
console.log(OrderId.is("anything"))   // => true            (always true)
```

### Brand.make

Builds a `Constructor` from a single validation predicate. The `filter` returns
`true` for a valid value, or a `string` message describing why it is invalid.

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

type NonEmpty = Brand.Branded<string, "NonEmpty">

const NonEmpty = Brand.make<NonEmpty>((s) =>
  s.length > 0 || "Expected a non-empty string"
)

console.log(NonEmpty("hi"))        // => "hi"
console.log(NonEmpty.option(""))   // => None
// NonEmpty("")                    // throws BrandError("Expected a non-empty string")
```

### Brand.check

Builds a `Constructor` from one or more [Schema](https://effect.plants.sh/schema/) checks. Checks
**accumulate**: every check must pass, and the failure message comes from the
schema issue.

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

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

const PositiveInt = Brand.check<PositiveInt>(
  Schema.isInt(),
  Schema.isGreaterThan(0)
)

console.log(PositiveInt(5))          // => 5
console.log(PositiveInt.option(2.5)) // => None  (fails isInt)
console.log(PositiveInt.option(-1))  // => None  (fails isGreaterThan(0))
console.log(PositiveInt.is(5))       // => true
```

### Brand.all

Combines multiple constructors that share the **same base type** into one
constructor; the combined checks must all pass. Use `Brand.Brand.FromConstructor`
to name the resulting branded type.

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

type Int = Brand.Branded<number, "Int">
const Int = Brand.check<Int>(Schema.isInt())

type Positive = Brand.Branded<number, "Positive">
const Positive = Brand.check<Positive>(Schema.isGreaterThan(0))

// Carries both brands: number & Brand<"Int"> & Brand<"Positive">
const PositiveInt = Brand.all(Int, Positive)
type PositiveInt = Brand.Brand.FromConstructor<typeof PositiveInt>

console.log(PositiveInt(5))          // => 5
console.log(PositiveInt.option(2.5)) // => None  (not an Int)
console.log(PositiveInt.option(-3))  // => None  (not Positive)
```

If none of the combined constructors carry runtime checks, `all` behaves like a
nominal constructor.

### Brand.Constructor\<B>

The callable interface shared by every brand constructor. Calling it directly
validates and **throws** a `BrandError`; the methods provide non-throwing forms.

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

type Int = Brand.Branded<number, "Int">
const Int = Brand.make<Int>((n) =>
  Number.isInteger(n) || `Expected ${n} to be an integer`
)
```

- **Call form — `Int(x)`**: returns `B`, throws `BrandError` on invalid input.

  ```ts
  console.log(Int(42)) // => 42
  // Int(1.5)          // throws BrandError("Expected 1.5 to be an integer")
  ```

- **`.option(x)`**: returns `Option<B>` — `Some` if valid, `None` otherwise.

  ```ts
  console.log(Int.option(42))  // => Some(42)
  console.log(Int.option(1.5)) // => None
  ```

- **`.result(x)`**: returns `Result<B, BrandError>` — `Success` if valid,
  `Failure` carrying the `BrandError` otherwise.

  ```ts
  console.log(Int.result(42))  // => Success(42)
  console.log(Int.result(1.5)) // => Failure(BrandError("Expected 1.5 to be an integer"))
  ```

- **`.is(x)`**: a type guard returning `boolean`; narrows the value to the
  branded type when `true`.

  ```ts
  const n: number = 42
  if (Int.is(n)) {
    // here `n` is narrowed to Int
  }
  console.log(Int.is(42))  // => true
  console.log(Int.is(1.5)) // => false
  ```

### Brand.BrandError

The error produced when construction fails. It is **not** a JavaScript `Error`
subclass — it is an error-like model carrying `_tag: "BrandError"`, `name`,
the underlying schema `issue`, and a `message` getter (the rendered issue).
Calling a constructor throws this value, and `.result` exposes it as the
failure.

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

type Int = Brand.Branded<number, "Int">
const Int = Brand.make<Int>((n) =>
  Number.isInteger(n) || `Expected ${n} to be an integer`
)

const r = Int.result(1.5)
if (Result.isFailure(r)) {
  const err = r.failure
  console.log(err._tag)        // => "BrandError"
  console.log(err.message)     // => "Expected 1.5 to be an integer"
  console.log(String(err))     // => "BrandError(Expected 1.5 to be an integer)"
}
```

### Brand.Brand namespace (type-level helpers)

The `Brand.Brand` namespace holds type-level utilities for working with brands
and constructors. The two you reach for most:

**`Brand.Brand.FromConstructor<C>`** — extracts the branded type produced by a
constructor `C`. Handy after `Brand.all`, or to avoid repeating a brand type.

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

const PositiveInt = Brand.check(Schema.isInt(), Schema.isGreaterThan(0))
type PositiveInt = Brand.Brand.FromConstructor<typeof PositiveInt>
// => number & Brand.Brand<...>
```

**`Brand.Brand.EnsureCommonBase<Brands>`** — the constraint `Brand.all` uses to
verify every constructor shares one base type. If they don't, it surfaces the
type error `"ERROR: All brands should have the same base type"`.

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

const Int = Brand.check(Schema.isInt())          // base: number
const Lower = Brand.check(Schema.isLowercased()) // base: string

// Brand.all(Int, Lower) // type error: bases (number vs string) differ
```

The remaining helpers are advanced type-level utilities you rarely write by
hand:

- **`Brand.Brand.Unbranded<B>`** — strips the brand to recover the underlying
  base type (`Brand.Brand.Unbranded<UserId>` is `number`). This is the type a
  constructor accepts as input.
- **`Brand.Brand.Keys<B>`** — the union of brand key strings on `B`.
- **`Brand.Brand.Brands<B>`** — the intersection of `Brand<K>` markers carried
  by `B`.

## See also

- [Branded types (code-style guide)](https://effect.plants.sh/code-style/branded-types/) — when and why
  to use brands.
- [Newtype](https://effect.plants.sh/data-types/newtype/) — wrapping a value in a distinct type when a
  phantom tag is not enough.
- [Schema](https://effect.plants.sh/schema/) — `Schema.brand` / `Schema.fromBrand` to apply brands during
  decoding.
- [Result](https://effect.plants.sh/data-types/result/) and [Option](https://effect.plants.sh/data-types/option/) — the return
  types of `.result` and `.option`.