Skip to content

Branded Types

In this guide, we will explore the concept of branded types in TypeScript and learn how to create and work with them using the Brand module. Branded types are TypeScript types with an added type tag that helps prevent accidental usage of a value in the wrong context. They allow us to create distinct types based on an existing underlying type, enabling type safety and better code organization.

TypeScript’s type system is structurally typed, meaning that two types are considered compatible if their members are compatible. This can lead to situations where values of the same underlying type are used interchangeably, even when they represent different concepts or have different meanings.

Consider the following types:

1
type
type UserId = number
UserId
= number
2
3
type
type ProductId = number
ProductId
= number

Here, UserId and ProductId are structurally identical as they are both based on number. TypeScript will treat these as interchangeable, potentially causing bugs if they are mixed up in your application.

Example (Unintended Type Compatibility)

1
type
type UserId = number
UserId
= number
2
3
type
type ProductId = number
ProductId
= number
4
5
const
const getUserById: (id: UserId) => void
getUserById
= (
(parameter) id: number
id
:
type UserId = number
UserId
) => {
6
// Logic to retrieve user
7
}
8
9
const
const getProductById: (id: ProductId) => void
getProductById
= (
(parameter) id: number
id
:
type ProductId = number
ProductId
) => {
10
// Logic to retrieve product
11
}
12
13
const
const id: number
id
:
type UserId = number
UserId
= 1
14
15
const getProductById: (id: ProductId) => void
getProductById
(
const id: number
id
) // No type error, but incorrect usage

In the example above, passing a UserId to getProductById does not produce a type error, even though it’s logically incorrect. This happens because both types are considered interchangeable.

Branded types allow you to create distinct types from the same underlying type by adding a unique type tag, enforcing proper usage at compile-time.

Branding is accomplished by adding a symbolic identifier that distinguishes one type from another at the type level. This method ensures that types remain distinct without altering their runtime characteristics.

Let’s start by introducing the BrandTypeId symbol:

1
const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
(method) SymbolConstructor.for(key: string): symbol

Returns a Symbol object from the global symbol registry matching the given key if found. Otherwise, returns a new symbol with this key.

for
("effect/Brand")
2
3
type
type ProductId = number & { readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }
ProductId
= number & {
4
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
5
readonly
(property) ProductId: "ProductId"
ProductId
: "ProductId" // unique identifier for ProductId
6
}
7
}

This approach assigns a unique identifier as a brand to the number type, effectively differentiating ProductId from other numerical types. The use of a symbol ensures that the branding field does not conflict with any existing properties of the number type.

Attempting to use a UserId in place of a ProductId now results in an error:

Example (Enforcing Type Safety with Branded Types)

1
const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
(method) SymbolConstructor.for(key: string): symbol

Returns a Symbol object from the global symbol registry matching the given key if found. Otherwise, returns a new symbol with this key.

for
("effect/Brand")
2
3
type
type ProductId = number & { readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }
ProductId
= number & {
4
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
5
readonly
(property) ProductId: "ProductId"
ProductId
: "ProductId"
6
}
7
}
8
9
const
const getProductById: (id: ProductId) => void
getProductById
= (
(parameter) id: ProductId
id
:
type ProductId = number & { readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }
ProductId
) => {
10
// Logic to retrieve product
11
}
12
13
type
type UserId = number
UserId
= number
14
15
const
const id: number
id
:
type UserId = number
UserId
= 1
16
17
// @ts-expect-error
18
const getProductById: (id: ProductId) => void
getProductById
(
const id: number
id
)
19
/*
20
Argument of type 'number' is not assignable to parameter of type 'ProductId'.
21
Type 'number' is not assignable to type '{ readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }'.ts(2345)
22
*/

The error message clearly states that a number cannot be used in place of a ProductId.

TypeScript won’t let us pass an instance of number to the function accepting ProductId because it’s missing the brand field.

Let’s add branding to UserId as well:

Example (Branding UserId and ProductId)

1
const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
(method) SymbolConstructor.for(key: string): symbol

Returns a Symbol object from the global symbol registry matching the given key if found. Otherwise, returns a new symbol with this key.

for
("effect/Brand")
2
3
type
type ProductId = number & { readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }
ProductId
= number & {
4
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
5
readonly
(property) ProductId: "ProductId"
ProductId
: "ProductId" // unique identifier for ProductId
6
}
7
}
8
9
const
const getProductById: (id: ProductId) => void
getProductById
= (
(parameter) id: ProductId
id
:
type ProductId = number & { readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }
ProductId
) => {
10
// Logic to retrieve product
11
}
12
13
type
type UserId = number & { readonly [BrandTypeId]: { readonly UserId: "UserId"; }; }
UserId
= number & {
14
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
15
readonly
(property) UserId: "UserId"
UserId
: "UserId" // unique identifier for UserId
16
}
17
}
18
19
declare const
const id: UserId
id
:
type UserId = number & { readonly [BrandTypeId]: { readonly UserId: "UserId"; }; }
UserId
20
21
// @ts-expect-error
22
const getProductById: (id: ProductId) => void
getProductById
(
const id: UserId
id
)
23
/*
24
Argument of type 'UserId' is not assignable to parameter of type 'ProductId'.
25
Type 'UserId' is not assignable to type '{ readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }'.
26
Types of property '[BrandTypeId]' are incompatible.
27
Property 'ProductId' is missing in type '{ readonly UserId: "UserId"; }' but required in type '{ readonly ProductId: "ProductId"; }'.ts(2345)
28
*/

The error indicates that while both types use branding, the unique values associated with the branding fields ("ProductId" and "UserId") ensure they remain distinct and non-interchangeable.

To enhance the versatility and reusability of branded types, they can be generalized using a standardized approach:

1
const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
(method) SymbolConstructor.for(key: string): symbol

Returns a Symbol object from the global symbol registry matching the given key if found. Otherwise, returns a new symbol with this key.

for
("effect/Brand")
2
3
// Create a generic Brand interface using a unique identifier
4
interface
interface Brand<in out ID extends string | symbol>
Brand
<in out
(type parameter) ID in Brand<in out ID extends string | symbol>
ID
extends string | symbol> {
5
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
6
readonly [
(type parameter) id
id
in
(type parameter) ID in Brand<in out ID extends string | symbol>
ID
]:
(type parameter) ID in Brand<in out ID extends string | symbol>
ID
7
}
8
}
9
10
// Define a ProductId type branded with a unique identifier
11
type
type ProductId = number & Brand<"ProductId">
ProductId
= number &
interface Brand<in out ID extends string | symbol>
Brand
<"ProductId">
12
13
// Define a UserId type branded similarly
14
type
type UserId = number & Brand<"UserId">
UserId
= number &
interface Brand<in out ID extends string | symbol>
Brand
<"UserId">

This design allows any type to be branded using a unique identifier, either a string or symbol.

Here’s how you can utilize the Brand interface, which is readily available from the Brand module, eliminating the need to craft your own implementation:

Example (Using the Brand Interface from the Brand Module)

1
import { Brand } from "effect"
2
3
// Define a ProductId type branded with a unique identifier
4
type ProductId = number & Brand.Brand<"ProductId">
5
6
// Define a UserId type branded similarly
7
type UserId = number & Brand.Brand<"UserId">

However, creating instances of these types directly leads to an error because the type system expects the brand structure:

Example (Direct Assignment Error)

1
const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
(method) SymbolConstructor.for(key: string): symbol

Returns a Symbol object from the global symbol registry matching the given key if found. Otherwise, returns a new symbol with this key.

for
("effect/Brand")
2
3
interface
interface Brand<in out K extends string | symbol>
Brand
<in out
(type parameter) K in Brand<in out K extends string | symbol>
K
extends string | symbol> {
4
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
5
readonly [
(type parameter) k
k
in
(type parameter) K in Brand<in out K extends string | symbol>
K
]:
(type parameter) K in Brand<in out K extends string | symbol>
K
6
}
7
}
8
9
type
type ProductId = number & Brand<"ProductId">
ProductId
= number &
interface Brand<in out K extends string | symbol>
Brand
<"ProductId">
10
11
// @ts-expect-error
12
const
const id: ProductId
id
:
type ProductId = number & Brand<"ProductId">
ProductId
= 1
13
/*
14
Type 'number' is not assignable to type 'ProductId'.
15
Type 'number' is not assignable to type 'Brand<"ProductId">'.ts(2322)
16
*/

You cannot directly assign a number to ProductId. The Brand module provides utilities to correctly construct values of branded types.

The Brand module provides two main functions for creating branded types: nominal and refined.

The Brand.nominal function is designed for defining branded types that do not require runtime validations. It simply adds a type tag to the underlying type, allowing us to distinguish between values of the same type but with different meanings. Nominal branded types are useful when we only want to create distinct types for clarity and code organization purposes.

Example (Defining Distinct Identifiers with Nominal Branding)

1
import {
import Brand
Brand
} from "effect"
2
3
// Define UserId as a branded number
4
type
type UserId = number & Brand.Brand<"UserId">
UserId
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"UserId">
5
6
// Constructor for UserId
7
const
const UserId: Brand.Brand.Constructor<UserId>
UserId
=
import Brand
Brand
.
const nominal: <UserId>() => Brand.Brand<in out K extends string | symbol>.Constructor<UserId>

This function returns a `Brand.Constructor` that **does not apply any runtime checks**, it just returns the provided value. It can be used to create nominal types that allow distinguishing between two values of the same type but with different meanings. If you also want to perform some validation, see {@link refined } .

nominal
<
type UserId = number & Brand.Brand<"UserId">
UserId
>()
8
9
const
const getUserById: (id: UserId) => void
getUserById
= (
(parameter) id: UserId
id
:
type UserId = number & Brand.Brand<"UserId">
UserId
) => {
10
// Logic to retrieve user
11
}
12
13
// Define ProductId as a branded number
14
type
type ProductId = number & Brand.Brand<"ProductId">
ProductId
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"ProductId">
15
16
// Constructor for ProductId
17
const
const ProductId: Brand.Brand.Constructor<ProductId>
ProductId
=
import Brand
Brand
.
const nominal: <ProductId>() => Brand.Brand<in out K extends string | symbol>.Constructor<ProductId>

This function returns a `Brand.Constructor` that **does not apply any runtime checks**, it just returns the provided value. It can be used to create nominal types that allow distinguishing between two values of the same type but with different meanings. If you also want to perform some validation, see {@link refined } .

nominal
<
type ProductId = number & Brand.Brand<"ProductId">
ProductId
>()
18
19
const
const getProductById: (id: ProductId) => void
getProductById
= (
(parameter) id: ProductId
id
:
type ProductId = number & Brand.Brand<"ProductId">
ProductId
) => {
20
// Logic to retrieve product
21
}

Attempting to assign a non-ProductId value will result in a compile-time error:

Example (Type Safety with Branded Identifiers)

1
import {
import Brand
Brand
} from "effect"
2
3
type
type UserId = number & Brand.Brand<"UserId">
UserId
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"UserId">
4
5
const
const UserId: Brand.Brand.Constructor<UserId>
UserId
=
import Brand
Brand
.
const nominal: <UserId>() => Brand.Brand<in out K extends string | symbol>.Constructor<UserId>

This function returns a `Brand.Constructor` that **does not apply any runtime checks**, it just returns the provided value. It can be used to create nominal types that allow distinguishing between two values of the same type but with different meanings. If you also want to perform some validation, see {@link refined } .

nominal
<
type UserId = number & Brand.Brand<"UserId">
UserId
>()
6
7
const
const getUserById: (id: UserId) => void
getUserById
= (
(parameter) id: UserId
id
:
type UserId = number & Brand.Brand<"UserId">
UserId
) => {
8
// Logic to retrieve user
9
}
10
11
type
type ProductId = number & Brand.Brand<"ProductId">
ProductId
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"ProductId">
12
13
const
const ProductId: Brand.Brand.Constructor<ProductId>
ProductId
=
import Brand
Brand
.
const nominal: <ProductId>() => Brand.Brand<in out K extends string | symbol>.Constructor<ProductId>

This function returns a `Brand.Constructor` that **does not apply any runtime checks**, it just returns the provided value. It can be used to create nominal types that allow distinguishing between two values of the same type but with different meanings. If you also want to perform some validation, see {@link refined } .

nominal
<
type ProductId = number & Brand.Brand<"ProductId">
ProductId
>()
14
15
const
const getProductById: (id: ProductId) => void
getProductById
= (
(parameter) id: ProductId
id
:
type ProductId = number & Brand.Brand<"ProductId">
ProductId
) => {
16
// Logic to retrieve product
17
}
18
19
// Correct usage
20
const getProductById: (id: ProductId) => void
getProductById
(
const ProductId: Brand.Brand<in out K extends string | symbol>.Constructor (args: number) => ProductId

Constructs a branded type from a value of type `A`, throwing an error if the provided `A` is not valid.

ProductId
(1))
21
22
// Incorrect, will result in an error
23
// @ts-expect-error
24
const getProductById: (id: ProductId) => void
getProductById
(1)
25
/*
26
Argument of type 'number' is not assignable to parameter of type 'ProductId'.
27
Type 'number' is not assignable to type 'Brand<"ProductId">'.ts(2345)
28
*/
29
30
// Also incorrect, will result in an error
31
// @ts-expect-error
32
const getProductById: (id: ProductId) => void
getProductById
(
const UserId: Brand.Brand<in out K extends string | symbol>.Constructor (args: number) => UserId

Constructs a branded type from a value of type `A`, throwing an error if the provided `A` is not valid.

UserId
(1))
33
/*
34
Argument of type 'UserId' is not assignable to parameter of type 'ProductId'.
35
Type 'UserId' is not assignable to type 'Brand<"ProductId">'.
36
Types of property '[BrandTypeId]' are incompatible.
37
Property 'ProductId' is missing in type '{ readonly UserId: "UserId"; }' but required in type '{ readonly ProductId: "ProductId"; }'.ts(2345)
38
*/

The Brand.refined function enables the creation of branded types that include data validation. It requires a refinement predicate to check the validity of input data against specific criteria.

When the input data does not meet the criteria, the function uses Brand.error to generate a BrandErrors data type. This provides detailed information about why the validation failed.

Example (Creating a Branded Type with Validation)

1
import {
import Brand
Brand
} from "effect"
2
3
// Define a branded type 'Int' to represent integer values
4
type
type Int = number & Brand.Brand<"Int">
Int
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"Int">
5
6
// Define the constructor using 'refined' to enforce integer values
7
const
const Int: Brand.Brand.Constructor<Int>
Int
=
import Brand
Brand
.
function refined<Int>(refinement: Predicate<number>, onFailure: (unbranded: number) => Brand.Brand<in out K extends string | symbol>.BrandErrors): Brand.Brand<in out K extends string | symbol>.Constructor<Int> (+1 overload)

Returns a `Brand.Constructor` that can construct a branded type from an unbranded value using the provided `refinement` predicate as validation of the input data. If you don't want to perform any validation but only distinguish between two values of the same type but with different meanings, see {@link nominal } .

refined
<
type Int = number & Brand.Brand<"Int">
Int
>(
8
// Validation to ensure the value is an integer
9
(
(parameter) n: number
n
) =>
var Number: NumberConstructor

An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.

Number
.
(method) NumberConstructor.isInteger(number: unknown): boolean

Returns true if the value passed is an integer, false otherwise.

isInteger
(
(parameter) n: number
n
),
10
// Provide an error if validation fails
11
(
(parameter) n: number
n
) =>
import Brand
Brand
.
const error: (message: string, meta?: unknown) => Brand.Brand<in out K extends string | symbol>.BrandErrors

Returns a `BrandErrors` that contains a single `RefinementError`.

error
(`Expected ${
(parameter) n: number
n
} to be an integer`)
12
)

Example (Using the Int Constructor)

1
import {
import Brand
Brand
} from "effect"
2
3
type
type Int = number & Brand.Brand<"Int">
Int
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"Int">
4
5
const
const Int: Brand.Brand.Constructor<Int>
Int
=
import Brand
Brand
.
function refined<Int>(refinement: Predicate<number>, onFailure: (unbranded: number) => Brand.Brand<in out K extends string | symbol>.BrandErrors): Brand.Brand<in out K extends string | symbol>.Constructor<Int> (+1 overload)

Returns a `Brand.Constructor` that can construct a branded type from an unbranded value using the provided `refinement` predicate as validation of the input data. If you don't want to perform any validation but only distinguish between two values of the same type but with different meanings, see {@link nominal } .

refined
<
type Int = number & Brand.Brand<"Int">
Int
>(
6
// Check if the value is an integer
7
(
(parameter) n: number
n
) =>
var Number: NumberConstructor

An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.

Number
.
(method) NumberConstructor.isInteger(number: unknown): boolean

Returns true if the value passed is an integer, false otherwise.

isInteger
(
(parameter) n: number
n
),
8
// Error message if the value is not an integer
9
(
(parameter) n: number
n
) =>
import Brand
Brand
.
const error: (message: string, meta?: unknown) => Brand.Brand<in out K extends string | symbol>.BrandErrors

Returns a `BrandErrors` that contains a single `RefinementError`.

error
(`Expected ${
(parameter) n: number
n
} to be an integer`)
10
)
11
12
// Create a valid Int value
13
const
const x: Int
x
:
type Int = number & Brand.Brand<"Int">
Int
=
const Int: Brand.Brand<in out K extends string | symbol>.Constructor (args: number) => Int

Constructs a branded type from a value of type `A`, throwing an error if the provided `A` is not valid.

Int
(3)
14
namespace console var console: Console

The `console` module provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers. The module exports two specific components: * A `Console` class with methods such as `console.log()`, `console.error()` and `console.warn()` that can be used to write to any Node.js stream. * A global `console` instance configured to write to [`process.stdout`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstdout) and [`process.stderr`](https://nodejs.org/docs/latest-v22.x/api/process.html#processstderr). The global `console` can be used without importing the `node:console` module. _**Warning**_: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the [`note on process I/O`](https://nodejs.org/docs/latest-v22.x/api/process.html#a-note-on-process-io) for more information. Example using the global `console`: ```js console.log('hello world'); // Prints: hello world, to stdout console.log('hello %s', 'world'); // Prints: hello world, to stdout console.error(new Error('Whoops, something bad happened')); // Prints error message and stack trace to stderr: // Error: Whoops, something bad happened // at [eval]:5:15 // at Script.runInThisContext (node:vm:132:18) // at Object.runInThisContext (node:vm:309:38) // at node:internal/process/execution:77:19 // at [eval]-wrapper:6:22 // at evalScript (node:internal/process/execution:76:60) // at node:internal/main/eval_string:23:3 const name = 'Will Robinson'; console.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to stderr ``` Example using the `Console` class: ```js const out = getStreamSomehow(); const err = getStreamSomehow(); const myConsole = new console.Console(out, err); myConsole.log('hello world'); // Prints: hello world, to out myConsole.log('hello %s', 'world'); // Prints: hello world, to out myConsole.error(new Error('Whoops, something bad happened')); // Prints: [Error: Whoops, something bad happened], to err const name = 'Will Robinson'; myConsole.warn(`Danger ${name}! Danger!`); // Prints: Danger Will Robinson! Danger!, to err ```

console
.
(method) Console.log(message?: any, ...optionalParams: any[]): void

Prints to `stdout` with newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar to [`printf(3)`](http://man7.org/linux/man-pages/man3/printf.3.html) (the arguments are all passed to [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args)). ```js const count = 5; console.log('count: %d', count); // Prints: count: 5, to stdout console.log('count:', count); // Prints: count: 5, to stdout ``` See [`util.format()`](https://nodejs.org/docs/latest-v22.x/api/util.html#utilformatformat-args) for more information.

log
(
const x: Int
x
) // Output: 3
15
16
// Attempt to create an Int with an invalid value
17
const
const y: Int
y
:
type Int = number & Brand.Brand<"Int">
Int
=
const Int: Brand.Brand<in out K extends string | symbol>.Constructor (args: number) => Int

Constructs a branded type from a value of type `A`, throwing an error if the provided `A` is not valid.

Int
(3.14)
18
// throws [ { message: 'Expected 3.14 to be an integer' } ]

Attempting to assign a non-Int value will result in a compile-time error:

Example (Compile-Time Error for Incorrect Assignments)

1
import {
import Brand
Brand
} from "effect"
2
3
type
type Int = number & Brand.Brand<"Int">
Int
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"Int">
4
5
const
const Int: Brand.Brand.Constructor<Int>
Int
=
import Brand
Brand
.
function refined<Int>(refinement: Predicate<number>, onFailure: (unbranded: number) => Brand.Brand<in out K extends string | symbol>.BrandErrors): Brand.Brand<in out K extends string | symbol>.Constructor<Int> (+1 overload)

Returns a `Brand.Constructor` that can construct a branded type from an unbranded value using the provided `refinement` predicate as validation of the input data. If you don't want to perform any validation but only distinguish between two values of the same type but with different meanings, see {@link nominal } .

refined
<
type Int = number & Brand.Brand<"Int">
Int
>(
6
(
(parameter) n: number
n
) =>
var Number: NumberConstructor

An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.

Number
.
(method) NumberConstructor.isInteger(number: unknown): boolean

Returns true if the value passed is an integer, false otherwise.

isInteger
(
(parameter) n: number
n
),
7
(
(parameter) n: number
n
) =>
import Brand
Brand
.
const error: (message: string, meta?: unknown) => Brand.Brand<in out K extends string | symbol>.BrandErrors

Returns a `BrandErrors` that contains a single `RefinementError`.

error
(`Expected ${
(parameter) n: number
n
} to be an integer`)
8
)
9
10
// Correct usage
11
const
const good: Int
good
:
type Int = number & Brand.Brand<"Int">
Int
=
const Int: Brand.Brand<in out K extends string | symbol>.Constructor (args: number) => Int

Constructs a branded type from a value of type `A`, throwing an error if the provided `A` is not valid.

Int
(3)
12
13
// Incorrect, will result in an error
14
// @ts-expect-error
15
const
const bad1: Int
bad1
:
type Int = number & Brand.Brand<"Int">
Int
= 3
16
17
// Also incorrect, will result in an error
18
// @ts-expect-error
19
const
const bad2: Int
bad2
:
type Int = number & Brand.Brand<"Int">
Int
= 3.14

In some cases, you might need to combine multiple branded types. The Brand module provides the Brand.all API for this purpose:

Example (Combining Multiple Branded Types)

1
import {
import Brand
Brand
} from "effect"
2
3
type
type Int = number & Brand.Brand<"Int">
Int
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"Int">
4
5
const
const Int: Brand.Brand.Constructor<Int>
Int
=
import Brand
Brand
.
function refined<Int>(refinement: Predicate<number>, onFailure: (unbranded: number) => Brand.Brand<in out K extends string | symbol>.BrandErrors): Brand.Brand<in out K extends string | symbol>.Constructor<Int> (+1 overload)

Returns a `Brand.Constructor` that can construct a branded type from an unbranded value using the provided `refinement` predicate as validation of the input data. If you don't want to perform any validation but only distinguish between two values of the same type but with different meanings, see {@link nominal } .

refined
<
type Int = number & Brand.Brand<"Int">
Int
>(
6
(
(parameter) n: number
n
) =>
var Number: NumberConstructor

An object that represents a number of any kind. All JavaScript numbers are 64-bit floating-point numbers.

Number
.
(method) NumberConstructor.isInteger(number: unknown): boolean

Returns true if the value passed is an integer, false otherwise.

isInteger
(
(parameter) n: number
n
),
7
(
(parameter) n: number
n
) =>
import Brand
Brand
.
const error: (message: string, meta?: unknown) => Brand.Brand<in out K extends string | symbol>.BrandErrors

Returns a `BrandErrors` that contains a single `RefinementError`.

error
(`Expected ${
(parameter) n: number
n
} to be an integer`)
8
)
9
10
type
type Positive = number & Brand.Brand<"Positive">
Positive
= number &
import Brand
Brand
.
interface Brand<in out K extends string | symbol> namespace Brand

A generic interface that defines a branded type.

Brand
<"Positive">
11
12
const
const Positive: Brand.Brand.Constructor<Positive>
Positive
=
import Brand
Brand
.
function refined<Positive>(refinement: Predicate<number>, onFailure: (unbranded: number) => Brand.Brand<in out K extends string | symbol>.BrandErrors): Brand.Brand<in out K extends string | symbol>.Constructor<Positive> (+1 overload)

Returns a `Brand.Constructor` that can construct a branded type from an unbranded value using the provided `refinement` predicate as validation of the input data. If you don't want to perform any validation but only distinguish between two values of the same type but with different meanings, see {@link nominal } .

refined
<
type Positive = number & Brand.Brand<"Positive">
Positive
>(
13
(
(parameter) n: number
n
) =>
(parameter) n: number
n
> 0,
14
(
(parameter) n: number
n
) =>
import Brand
Brand
.
const error: (message: string, meta?: unknown) => Brand.Brand<in out K extends string | symbol>.BrandErrors

Returns a `BrandErrors` that contains a single `RefinementError`.

error
(`Expected ${
(parameter) n: number
n
} to be positive`)
15
)
16
17
// Combine the Int and Positive constructors
18
// into a new branded constructor PositiveInt
19
const
const PositiveInt: Brand.Brand.Constructor<number & Brand.Brand<"Int"> & Brand.Brand<"Positive">>
PositiveInt
=
import Brand
Brand
.
const all: <[Brand.Brand<in out K extends string | symbol>.Constructor<Int>, Brand.Brand.Constructor<Positive>]>(brands_0: Brand.Brand.Constructor<Int>, brands_1: Brand.Brand.Constructor<...>) => Brand.Brand.Constructor<...>

Combines two or more brands together to form a single branded type. This API is useful when you want to validate that the input data passes multiple brand validators.

all
(
const Int: Brand.Brand.Constructor<Int>
Int
,
const Positive: Brand.Brand.Constructor<Positive>
Positive
)
20
21
// Extract the branded type from the PositiveInt constructor
22
type
type PositiveInt = number & Brand.Brand<"Int"> & Brand.Brand<"Positive">
PositiveInt
=
import Brand
Brand
.
namespace Brand

A generic interface that defines a branded type.

Brand
.
type Brand<in out K extends string | symbol>.FromConstructor<A> = A extends Brand.Brand.Constructor<infer B extends Brand.Brand<any>> ? B : never

A utility type to extract a branded type from a `Brand.Constructor`.

FromConstructor
<typeof
const PositiveInt: Brand.Brand.Constructor<number & Brand.Brand<"Int"> & Brand.Brand<"Positive">>
PositiveInt
>
23
24
// Usage example
25
26
// Valid positive integer
27
const
const good: number & Brand.Brand<"Int"> & Brand.Brand<"Positive">
good
:
type PositiveInt = number & Brand.Brand<"Int"> & Brand.Brand<"Positive">
PositiveInt
=
const PositiveInt: Brand.Brand<in out K extends string | symbol>.Constructor (args: number) => number & Brand.Brand<"Int"> & Brand.Brand<"Positive">

Constructs a branded type from a value of type `A`, throwing an error if the provided `A` is not valid.

PositiveInt
(10)
28
29
// throws [ { message: 'Expected -5 to be positive' } ]
30
const
const bad1: number & Brand.Brand<"Int"> & Brand.Brand<"Positive">
bad1
:
type PositiveInt = number & Brand.Brand<"Int"> & Brand.Brand<"Positive">
PositiveInt
=
const PositiveInt: Brand.Brand<in out K extends string | symbol>.Constructor (args: number) => number & Brand.Brand<"Int"> & Brand.Brand<"Positive">

Constructs a branded type from a value of type `A`, throwing an error if the provided `A` is not valid.

PositiveInt
(-5)
31
32
// throws [ { message: 'Expected 3.14 to be an integer' } ]
33
const
const bad2: number & Brand.Brand<"Int"> & Brand.Brand<"Positive">
bad2
:
type PositiveInt = number & Brand.Brand<"Int"> & Brand.Brand<"Positive">
PositiveInt
=
const PositiveInt: Brand.Brand<in out K extends string | symbol>.Constructor (args: number) => number & Brand.Brand<"Int"> & Brand.Brand<"Positive">

Constructs a branded type from a value of type `A`, throwing an error if the provided `A` is not valid.

PositiveInt
(3.14)