Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

Newtype unstable

Creates compile-time-only wrappers around existing value types.

A newtype lets TypeScript distinguish values with the same runtime shape, such as two different ids that are both strings. The tag exists only in the type system, so wrapping does not allocate a runtime object. This module includes the base Newtype interface, wrapping and unwrapping helpers, optics, and helpers for reusing carrier instances such as Equivalence, Order, Combiner, and Reducer.

8 exports Since v4.0.0 Source

Constructors

Since v4.0.0

Lifts a Combiner for the carrier type into a Combiner for the newtype.

When to use

Use when you need to combine newtype-wrapped values with the carrier's combining logic, without manually unwrapping.

Details

The returned combiner delegates to the provided carrier combiner.

See

Signature

declare const makeCombiner: <N extends Newtype.Any>(combiner: Combiner.Combiner<Newtype.Carrier<N>>) => Combiner.Combiner<N>

Example Combining newtypes

import { Combiner, Newtype } from "effect"
interface Amount extends Newtype.Newtype<"Amount", number> {}
const sum = Combiner.make<number>((a, b) => a + b)
const combiner = Newtype.makeCombiner<Amount>(sum)
const iso = Newtype.makeIso<Amount>()
const total = combiner.combine(iso.set(10), iso.set(20))
Newtype.value(total) // => 30
Since v4.0.0

Lifts an Equivalence for the carrier type into an Equivalence for the newtype.

When to use

Use when you need equality for newtype-wrapped values to behave like equality for the wrapped carrier value, without manually unwrapping.

Details

The returned equivalence delegates to the provided carrier equivalence and has zero runtime cost beyond the underlying equivalence check.

See

  • makeOrder โ€” lift an Order for the carrier

Signature

declare const makeEquivalence: <N extends Newtype.Any>(equivalence: Equivalence.Equivalence<Newtype.Carrier<N>>) => Equivalence.Equivalence<N>

Example Comparing newtypes

import { Equivalence, Newtype } from "effect"
interface Label extends Newtype.Newtype<"Label", string> {}
const eq = Newtype.makeEquivalence<Label>(Equivalence.String)
const iso = Newtype.makeIso<Label>()
eq(iso.set("a"), iso.set("a")) // => true
eq(iso.set("a"), iso.set("b")) // => false

makeIso

unstable
Since v4.0.0

Creates an Optic.Iso for a newtype, providing both wrapping (set) and unwrapping (get).

When to use

Use as the primary way to construct and deconstruct newtype values.

Details

The returned iso composes with other optics via the standard Optic API. Both directions have zero runtime cost because they are identity casts.

See

  • value โ€” unwrap only

Signature

declare function makeIso<N extends Any>(): Iso<N, Carrier<N>>

Example Wrapping and unwrapping with an iso

import { Newtype } from "effect"
interface Label extends Newtype.Newtype<"Label", string> {}
const labelIso = Newtype.makeIso<Label>()
const label: Label = labelIso.set("world")
const str: string = labelIso.get(label)
str // => "world"

makeOrder

unstable
Since v4.0.0

Lifts an Order for the carrier type into an Order for the newtype.

When to use

Use when you need to sort newtype-wrapped values according to the ordering of the wrapped carrier value, without manually unwrapping.

Details

The returned order delegates to the provided carrier order.

See

Signature

declare const makeOrder: <N extends Newtype.Any>(order: Order.Order<Newtype.Carrier<N>>) => Order.Order<N>

Example Ordering newtypes

import { Newtype, Order } from "effect"
interface Score extends Newtype.Newtype<"Score", number> {}
const ord = Newtype.makeOrder<Score>(Order.Number)
const iso = Newtype.makeIso<Score>()
ord(iso.set(1), iso.set(2)) // => -1

makeReducer

unstable
Since v4.0.0

Lifts a Reducer for the carrier type into a Reducer for the newtype.

When to use

Use when you need to reduce a collection of newtype-wrapped values with the carrier's reducer, without manually unwrapping.

Details

The returned reducer delegates to the provided carrier reducer.

See

Signature

declare const makeReducer: <N extends Newtype.Any>(reducer: Reducer.Reducer<Newtype.Carrier<N>>) => Reducer.Reducer<N>

Example Reducing newtypes

import { Newtype, Reducer } from "effect"
interface Score extends Newtype.Newtype<"Score", number> {}
const sum = Reducer.make<number>((a, b) => a + b, 0)
const reducer = Newtype.makeReducer<Score>(sum)
const iso = Newtype.makeIso<Score>()
const total = reducer.combineAll([iso.set(1), iso.set(2), iso.set(3)])
Newtype.value(total) // => 6

Getters

value

unstable
Since v4.0.0

Unwraps a newtype value, returning the underlying carrier value.

When to use

Use when you need the carrier value from an existing newtype without constructing a new newtype value at the same call site.

Details

This has zero runtime cost because it is an identity cast.

See

  • makeIso โ€” two-way conversion (wrap and unwrap)

Signature

declare const value: <N extends Newtype.Any>(newtype: N) => Newtype.Carrier<N>

Example Unwrapping a newtype

import { Newtype } from "effect"
interface Label extends Newtype.Newtype<"Label", string> {}
const iso = Newtype.makeIso<Label>()
const label = iso.set("hello")
const raw: string = Newtype.value(label)
raw // => "hello"

Models

Newtype

interface unstable
Since v4.0.0

A tagged interface that wraps a carrier type under a unique key, preventing accidental interchange of structurally identical values.

When to use

Use to define a newtype as an interface extending Newtype<"MyKey", CarrierType> when structurally identical carrier types should remain distinct in TypeScript.

Details

The tag is compile-time only, so no runtime wrapper is allocated. Use makeIso to create a two-way conversion, or value to unwrap.

See

  • makeIso โ€” create an iso to wrap and unwrap
  • value โ€” unwrap a newtype value

Signature

interface Newtype<in out Key extends string, out Carrier> {
readonly "~effect/Newtype": {
readonly carrier: Carrier;
readonly key: Key;
};
}

Example Defining a newtype

import { Newtype } from "effect"
interface UserId extends Newtype.Newtype<"UserId", number> {}
interface OrderId extends Newtype.Newtype<"OrderId", number> {}
const userId = Newtype.makeIso<UserId>().set(1)
// UserId and OrderId are not assignable to each other
// even though both wrap `number`.
// @ts-expect-error
const orderId: OrderId = userId
Newtype.value(userId) // => 1

Other

Newtype

unstable
Since v4.0.0

Namespace containing type-level helpers for Newtype values, including constraints and utilities for extracting a newtype's key and carrier type.

When to use

Use to access generic constraints and type-level utilities for Newtype values.