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:

type
type UserId = number
UserId
= number
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)

type
type UserId = number
UserId
= number
type
type ProductId = number
ProductId
= number
const
const getUserById: (id: UserId) => void
getUserById
= (
id: number
id
:
type UserId = number
UserId
) => {
// Logic to retrieve user
}
const
const getProductById: (id: ProductId) => void
getProductById
= (
id: number
id
:
type ProductId = number
ProductId
) => {
// Logic to retrieve product
}
const
const id: number
id
:
type UserId = number
UserId
= 1
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:

const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
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.

@paramkey key to search for.

for
("effect/Brand")
type
type ProductId = number & {
readonly [BrandTypeId]: {
readonly ProductId: "ProductId";
};
}
ProductId
= number & {
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
readonly
type ProductId: "ProductId"
ProductId
: "ProductId" // unique identifier for ProductId
}
}

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)

const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
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.

@paramkey key to search for.

for
("effect/Brand")
type
type ProductId = number & {
readonly [BrandTypeId]: {
readonly ProductId: "ProductId";
};
}
ProductId
= number & {
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
readonly
type ProductId: "ProductId"
ProductId
: "ProductId"
}
}
const
const getProductById: (id: ProductId) => void
getProductById
= (
id: ProductId
id
:
type ProductId = number & {
readonly [BrandTypeId]: {
readonly ProductId: "ProductId";
};
}
ProductId
) => {
// Logic to retrieve product
}
type
type UserId = number
UserId
= number
const
const id: number
id
:
type UserId = number
UserId
= 1
// @ts-expect-error
const getProductById: (id: ProductId) => void
getProductById
(
const id: number
id
)
/*
Argument of type 'number' is not assignable to parameter of type 'ProductId'.
Type 'number' is not assignable to type '{ readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }'.ts(2345)
*/

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)

const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
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.

@paramkey key to search for.

for
("effect/Brand")
type
type ProductId = number & {
readonly [BrandTypeId]: {
readonly ProductId: "ProductId";
};
}
ProductId
= number & {
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
readonly
type ProductId: "ProductId"
ProductId
: "ProductId" // unique identifier for ProductId
}
}
const
const getProductById: (id: ProductId) => void
getProductById
= (
id: ProductId
id
:
type ProductId = number & {
readonly [BrandTypeId]: {
readonly ProductId: "ProductId";
};
}
ProductId
) => {
// Logic to retrieve product
}
type
type UserId = number & {
readonly [BrandTypeId]: {
readonly UserId: "UserId";
};
}
UserId
= number & {
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
readonly
type UserId: "UserId"
UserId
: "UserId" // unique identifier for UserId
}
}
declare const
const id: UserId
id
:
type UserId = number & {
readonly [BrandTypeId]: {
readonly UserId: "UserId";
};
}
UserId
// @ts-expect-error
const getProductById: (id: ProductId) => void
getProductById
(
const id: UserId
id
)
/*
Argument of type 'UserId' is not assignable to parameter of type 'ProductId'.
Type 'UserId' is not assignable to type '{ readonly [BrandTypeId]: { readonly ProductId: "ProductId"; }; }'.
Types of property '[BrandTypeId]' are incompatible.
Property 'ProductId' is missing in type '{ readonly UserId: "UserId"; }' but required in type '{ readonly ProductId: "ProductId"; }'.ts(2345)
*/

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:

const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
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.

@paramkey key to search for.

for
("effect/Brand")
// Create a generic Brand interface using a unique identifier
interface
interface Brand<in out ID extends string | symbol>
Brand
<in out
function (type parameter) ID in Brand<in out ID extends string | symbol>
ID
extends string | symbol> {
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
readonly [
function (type parameter) id
id
in
function (type parameter) ID in Brand<in out ID extends string | symbol>
ID
]:
function (type parameter) ID in Brand<in out ID extends string | symbol>
ID
}
}
// Define a ProductId type branded with a unique identifier
type
type ProductId = number & Brand<"ProductId">
ProductId
= number &
interface Brand<in out ID extends string | symbol>
Brand
<"ProductId">
// Define a UserId type branded similarly
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)

import { Brand } from "effect"
// Define a ProductId type branded with a unique identifier
type ProductId = number & Brand.Brand<"ProductId">
// Define a UserId type branded similarly
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)

const
const BrandTypeId: typeof BrandTypeId
BrandTypeId
: unique symbol =
var Symbol: SymbolConstructor
Symbol
.
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.

@paramkey key to search for.

for
("effect/Brand")
interface
interface Brand<in out K extends string | symbol>
Brand
<in out
function (type parameter) K in Brand<in out K extends string | symbol>
K
extends string | symbol> {
readonly [
const BrandTypeId: typeof BrandTypeId
BrandTypeId
]: {
readonly [
function (type parameter) k
k
in
function (type parameter) K in Brand<in out K extends string | symbol>
K
]:
function (type parameter) K in Brand<in out K extends string | symbol>
K
}
}
type
type ProductId = number & Brand<"ProductId">
ProductId
= number &
interface Brand<in out K extends string | symbol>
Brand
<"ProductId">
// @ts-expect-error
const
const id: ProductId
id
:
type ProductId = number & Brand<"ProductId">
ProductId
= 1
/*
Type 'number' is not assignable to type 'ProductId'.
Type 'number' is not assignable to type 'Brand<"ProductId">'.ts(2322)
*/

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)

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"UserId">
// Constructor for UserId
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

refined

.

@example

import { Brand } from "effect"
type UserId = number & Brand.Brand<"UserId">
const UserId = Brand.nominal<UserId>()
assert.strictEqual(UserId(1), 1)

@since2.0.0

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"ProductId">
// Constructor for ProductId
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

refined

.

@example

import { Brand } from "effect"
type UserId = number & Brand.Brand<"UserId">
const UserId = Brand.nominal<UserId>()
assert.strictEqual(UserId(1), 1)

@since2.0.0

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

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

Example (Type Safety with Branded Identifiers)

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"UserId">
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

refined

.

@example

import { Brand } from "effect"
type UserId = number & Brand.Brand<"UserId">
const UserId = Brand.nominal<UserId>()
assert.strictEqual(UserId(1), 1)

@since2.0.0

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"ProductId">
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

refined

.

@example

import { Brand } from "effect"
type UserId = number & Brand.Brand<"UserId">
const UserId = Brand.nominal<UserId>()
assert.strictEqual(UserId(1), 1)

@since2.0.0

nominal
<
type ProductId = number & Brand.Brand<"ProductId">
ProductId
>()
const
const getProductById: (id: ProductId) => void
getProductById
= (
id: ProductId
id
:
type ProductId = number & Brand.Brand<"ProductId">
ProductId
) => {
// Logic to retrieve product
}
// Correct usage
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))
// Incorrect, will result in an error
// @ts-expect-error
const getProductById: (id: ProductId) => void
getProductById
(1)
/*
Argument of type 'number' is not assignable to parameter of type 'ProductId'.
Type 'number' is not assignable to type 'Brand<"ProductId">'.ts(2345)
*/
// Also incorrect, will result in an error
// @ts-expect-error
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))
/*
Argument of type 'UserId' is not assignable to parameter of type 'ProductId'.
Type 'UserId' is not assignable to type 'Brand<"ProductId">'.
Types of property '[BrandTypeId]' are incompatible.
Property 'ProductId' is missing in type '{ readonly UserId: "UserId"; }' but required in type '{ readonly ProductId: "ProductId"; }'.ts(2345)
*/

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)

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"Int">
// Define the constructor using 'refined' to enforce integer values
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

nominal

.

@paramrefinement - The refinement predicate to apply to the unbranded value.

@paramonFailure - Takes the unbranded value that did not pass the refinement predicate and returns a BrandErrors.

@example

import { Brand } from "effect"
type Int = number & Brand.Brand<"Int">
const Int = Brand.refined<Int>(
(n) => Number.isInteger(n),
(n) => Brand.error(`Expected ${n} to be an integer`)
)
assert.strictEqual(Int(1), 1)
assert.throws(() => Int(1.1))

@since2.0.0

refined
<
type Int = number & Brand.Brand<"Int">
Int
>(
// Validation to ensure the value is an integer
(
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
.
NumberConstructor.isInteger(number: unknown): boolean

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

@paramnumber A numeric value.

isInteger
(
n: number
n
),
// Provide an error if validation fails
(
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.

@since2.0.0

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

Example (Using the Int Constructor)

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"Int">
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

nominal

.

@paramrefinement - The refinement predicate to apply to the unbranded value.

@paramonFailure - Takes the unbranded value that did not pass the refinement predicate and returns a BrandErrors.

@example

import { Brand } from "effect"
type Int = number & Brand.Brand<"Int">
const Int = Brand.refined<Int>(
(n) => Number.isInteger(n),
(n) => Brand.error(`Expected ${n} to be an integer`)
)
assert.strictEqual(Int(1), 1)
assert.throws(() => Int(1.1))

@since2.0.0

refined
<
type Int = number & Brand.Brand<"Int">
Int
>(
// Check if the value is an integer
(
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
.
NumberConstructor.isInteger(number: unknown): boolean

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

@paramnumber A numeric value.

isInteger
(
n: number
n
),
// Error message if the value is not an integer
(
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.

@since2.0.0

error
(`Expected ${
n: number
n
} to be an integer`)
)
// Create a valid Int value
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)
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 and process.stderr. 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 for more information.

Example using the global console:

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:

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

@seesource

console
.
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) (the arguments are all passed to util.format()).

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() for more information.

@sincev0.1.100

log
(
const x: Int
x
) // Output: 3
// Attempt to create an Int with an invalid value
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)
// 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)

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"Int">
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

nominal

.

@paramrefinement - The refinement predicate to apply to the unbranded value.

@paramonFailure - Takes the unbranded value that did not pass the refinement predicate and returns a BrandErrors.

@example

import { Brand } from "effect"
type Int = number & Brand.Brand<"Int">
const Int = Brand.refined<Int>(
(n) => Number.isInteger(n),
(n) => Brand.error(`Expected ${n} to be an integer`)
)
assert.strictEqual(Int(1), 1)
assert.throws(() => Int(1.1))

@since2.0.0

refined
<
type Int = number & Brand.Brand<"Int">
Int
>(
(
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
.
NumberConstructor.isInteger(number: unknown): boolean

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

@paramnumber A numeric value.

isInteger
(
n: number
n
),
(
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.

@since2.0.0

error
(`Expected ${
n: number
n
} to be an integer`)
)
// Correct usage
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)
// Incorrect, will result in an error
// @ts-expect-error
const
const bad1: Int
bad1
:
type Int = number & Brand.Brand<"Int">
Int
= 3
// Also incorrect, will result in an error
// @ts-expect-error
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)

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"Int">
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

nominal

.

@paramrefinement - The refinement predicate to apply to the unbranded value.

@paramonFailure - Takes the unbranded value that did not pass the refinement predicate and returns a BrandErrors.

@example

import { Brand } from "effect"
type Int = number & Brand.Brand<"Int">
const Int = Brand.refined<Int>(
(n) => Number.isInteger(n),
(n) => Brand.error(`Expected ${n} to be an integer`)
)
assert.strictEqual(Int(1), 1)
assert.throws(() => Int(1.1))

@since2.0.0

refined
<
type Int = number & Brand.Brand<"Int">
Int
>(
(
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
.
NumberConstructor.isInteger(number: unknown): boolean

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

@paramnumber A numeric value.

isInteger
(
n: number
n
),
(
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.

@since2.0.0

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

Brand
<"Positive">
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

nominal

.

@paramrefinement - The refinement predicate to apply to the unbranded value.

@paramonFailure - Takes the unbranded value that did not pass the refinement predicate and returns a BrandErrors.

@example

import { Brand } from "effect"
type Int = number & Brand.Brand<"Int">
const Int = Brand.refined<Int>(
(n) => Number.isInteger(n),
(n) => Brand.error(`Expected ${n} to be an integer`)
)
assert.strictEqual(Int(1), 1)
assert.throws(() => Int(1.1))

@since2.0.0

refined
<
type Positive = number & Brand.Brand<"Positive">
Positive
>(
(
n: number
n
) =>
n: number
n
> 0,
(
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.

@since2.0.0

error
(`Expected ${
n: number
n
} to be positive`)
)
// Combine the Int and Positive constructors
// into a new branded constructor PositiveInt
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.

@example

import { Brand } from "effect"
type Int = number & Brand.Brand<"Int">
const Int = Brand.refined<Int>(
(n) => Number.isInteger(n),
(n) => Brand.error(`Expected ${n} to be an integer`)
)
type Positive = number & Brand.Brand<"Positive">
const Positive = Brand.refined<Positive>(
(n) => n > 0,
(n) => Brand.error(`Expected ${n} to be positive`)
)
const PositiveInt = Brand.all(Int, Positive)
assert.strictEqual(PositiveInt(1), 1)
assert.throws(() => PositiveInt(1.1))

@since2.0.0

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

A generic interface that defines a branded type.

@since2.0.0

@since2.0.0

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.

@since2.0.0

FromConstructor
<typeof
const PositiveInt: Brand.Brand.Constructor<number & Brand.Brand<"Int"> & Brand.Brand<"Positive">>
PositiveInt
>
// Usage example
// Valid positive integer
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)
// throws [ { message: 'Expected -5 to be positive' } ]
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)
// throws [ { message: 'Expected 3.14 to be an integer' } ]
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)