Skip to content

Number & BigInt

The Number and BigInt modules add named, pipeable operations around plain JavaScript numeric values. Numbers stay native numbers (including NaN, Infinity, and floating-point behavior) and bigints stay native bigints — the modules do not introduce wrapper types. What they add is a consistent surface for arithmetic, comparisons, range checks, parsing, conversion, and aggregation.

import { Number, pipe } from "effect"
// Pipeable arithmetic
const result = pipe(
10,
Number.sum(5), // 15
Number.multiply(2), // 30
Number.subtract(6) // 24
)
console.log(result) // => 24

The central design choice in both modules: operations that could produce NaN, Infinity, throw, or silently lose precision instead return an Option. Each has an *Unsafe counterpart for when you already know the input is valid (and want the raw value or a thrown error).

import { Number, BigInt } from "effect"
// Safe — never produces NaN/Infinity, surfaces failure as Option.none()
Number.divide(6, 0) // => Option.none()
Number.parse("not a number") // => Option.none()
BigInt.fromString("a") // => Option.none()
BigInt.fromNumber(1.5) // => Option.none() (not an integer)
BigInt.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + 1n) // => Option.none() (precision loss)
BigInt.sqrt(-1n) // => Option.none()
// Unsafe — assumes valid input, throws on failure
Number.divideUnsafe(6, 3) // => 2
BigInt.divideUnsafe(6n, 3n) // => 2n
BigInt.sqrtUnsafe(16n) // => 4n

This pairing means you choose your error model explicitly: thread an Option through the happy path, or assert validity with the *Unsafe variant.

Almost every binary operation in both modules is dual: you can call it data-first (Number.sum(2, 3)) or data-last for use in a pipe (Number.sum(3) returns a function awaiting the value). See Dual APIs for the full convention.

import { Number, pipe } from "effect"
Number.sum(2, 3) // => 5 (data-first)
pipe(2, Number.sum(3)) // => 5 (data-last)

import { Number } from "effect"

The native JavaScript Number constructor, re-exported on the namespace. Follows native coercion rules — empty strings become 0, invalid strings become NaN. For safe parsing prefer parse.

import { Number as N } from "effect"
N.Number("42") // => 42
N.Number("3.14") // => 3.14
N.Number("") // => 0

Type guard that narrows an unknown value to number.

import { Number } from "effect"
Number.isNumber(2) // => true
Number.isNumber("2") // => false

Order instance for numbers, returning -1 | 0 | 1. Use it with sorting and comparison APIs that accept an ordering.

import { Number } from "effect"
Number.Order(1, 2) // => -1
Number.Order(2, 1) // => 1
Number.Order(1, 1) // => 0

Equivalence instance for numbers. Unlike ===, it treats NaN as equivalent to NaN.

import { Number } from "effect"
Number.Equivalence(1, 1) // => true
Number.Equivalence(1, 2) // => false
Number.Equivalence(NaN, NaN) // => true

Adds two numbers (dual).

import { Number } from "effect"
Number.sum(2, 3) // => 5

Multiplies two numbers (dual).

import { Number } from "effect"
Number.multiply(2, 3) // => 6

Subtracts the second number from the first (dual).

import { Number } from "effect"
Number.subtract(2, 3) // => -1

Safe division returning Option.none() only when the divisor is 0 (dual). Other JavaScript results, including NaN, still follow normal number semantics.

import { Number } from "effect"
Number.divide(6, 3) // => Option.some(2)
Number.divide(6, 0) // => Option.none()

Division that throws a RangeError("Division by zero") when the divisor is 0 (dual). Use when the divisor is known to be non-zero.

import { Number } from "effect"
Number.divideUnsafe(6, 3) // => 2
// Number.divideUnsafe(6, 0) throws RangeError("Division by zero")

Adds 1 to a number.

import { Number } from "effect"
Number.increment(2) // => 3

Subtracts 1 from a number.

import { Number } from "effect"
Number.decrement(3) // => 2

Returns the remainder of dividing the first operand by the second, taking the sign of the dividend (dual). Preserves decimal precision better than the raw JavaScript % operator for decimal operands.

import { Number } from "effect"
Number.remainder(2, 2) // => 0
Number.remainder(3, 2) // => 1
Number.remainder(-4, 2) // => -0

Classifies a number as negative (-1), zero (0), or positive (1).

import { Number } from "effect"
Number.sign(-5) // => -1
Number.sign(0) // => 0
Number.sign(5) // => 1

Rounds a number to the given number of decimal places (dual).

import { Number } from "effect"
Number.round(1.1234, 2) // => 1.12
Number.round(1.567, 2) // => 1.57

Rounds a number up to the next power of two (minimum 2).

import { Number } from "effect"
Number.nextPow2(5) // => 8
Number.nextPow2(17) // => 32

true if the first argument is strictly less than the second (dual).

import { Number } from "effect"
Number.isLessThan(2, 3) // => true
Number.isLessThan(3, 3) // => false

true if the first argument is less than or equal to the second (dual).

import { Number } from "effect"
Number.isLessThanOrEqualTo(3, 3) // => true
Number.isLessThanOrEqualTo(4, 3) // => false

true if the first argument is strictly greater than the second (dual).

import { Number } from "effect"
Number.isGreaterThan(4, 3) // => true
Number.isGreaterThan(3, 3) // => false

true if the first argument is greater than or equal to the second (dual).

import { Number } from "effect"
Number.isGreaterThanOrEqualTo(3, 3) // => true
Number.isGreaterThanOrEqualTo(2, 3) // => false

Checks whether a number falls inside an inclusive [minimum, maximum] range (dual).

import { Number } from "effect"
const between = Number.between({ minimum: 0, maximum: 5 })
between(3) // => true
between(-1) // => false
between(6) // => false

Forces a number into an inclusive [minimum, maximum] range, returning the boundary when out of range (dual).

import { Number } from "effect"
const clamp = Number.clamp({ minimum: 1, maximum: 5 })
clamp(3) // => 3
clamp(0) // => 1
clamp(6) // => 5

Returns the smaller of two numbers (dual).

import { Number } from "effect"
Number.min(2, 3) // => 2

Returns the larger of two numbers (dual).

import { Number } from "effect"
Number.max(2, 3) // => 3

Parses a number from a string without throwing, returning Option<number>. The strings "NaN", "Infinity", and "-Infinity" are supported; blank or invalid text yields Option.none().

import { Number } from "effect"
Number.parse("42") // => Option.some(42)
Number.parse("3.14") // => Option.some(3.14)
Number.parse("Infinity") // => Option.some(Infinity)
Number.parse("not a number") // => Option.none()
Number.parse("") // => Option.none()

Sums an iterable of numbers. Returns 0 for an empty iterable.

import { Number } from "effect"
Number.sumAll([2, 3, 4]) // => 9

Multiplies an iterable of numbers. Returns 1 for an empty iterable, and short-circuits to 0 as soon as it sees a 0.

import { Number } from "effect"
Number.multiplyAll([2, 3, 4]) // => 24

A Reducer packages a binary combine operation together with an identity value (and an optional optimized combineAll). It is the abstraction Effect collection APIs consume when they need to fold values. You can use one directly via reducer.combineAll(iterable), which folds starting from the identity — so an empty collection returns the identity rather than failing.

import { Number } from "effect"
// combineAll folds an iterable from the reducer's identity
Number.ReducerSum.combineAll([1, 2, 3]) // => 6
Number.ReducerSum.combineAll([]) // => 0 (identity)
// combine merges two values directly
Number.ReducerSum.combine(4, 5) // => 9

Combines numbers with addition; identity 0.

import { Number } from "effect"
Number.ReducerSum.combineAll([10, 20, 30]) // => 60
Number.ReducerSum.combineAll([]) // => 0

Combines numbers with multiplication; identity 1. Short-circuits to 0 when it encounters a 0.

import { Number } from "effect"
Number.ReducerMultiply.combineAll([2, 3, 4]) // => 24
Number.ReducerMultiply.combineAll([]) // => 1

Keeps the maximum value; identity -Infinity (so an empty collection returns -Infinity). NaN propagates through Math.max.

import { Number } from "effect"
Number.ReducerMax.combineAll([3, 1, 4, 1, 5]) // => 5
Number.ReducerMax.combineAll([]) // => -Infinity

Keeps the minimum value; identity Infinity (so an empty collection returns Infinity). NaN propagates through Math.min.

import { Number } from "effect"
Number.ReducerMin.combineAll([3, 1, 4, 1, 5]) // => 1
Number.ReducerMin.combineAll([]) // => Infinity

import { BigInt } from "effect"

Reach for BigInt when values may exceed JavaScript’s safe integer range, when conversions should make failure explicit with Option, or when collection APIs need bigint-specific ordering or combining. Use the n literal suffix for bigint values (10n).

The native BigInt constructor, re-exported on the namespace. Follows native coercion — it throws for invalid strings or non-integral numbers, and whitespace-only strings coerce to 0n. For safe conversion prefer fromString / fromNumber.

import { BigInt } from "effect"
BigInt.BigInt(123) // => 123n
BigInt.BigInt("456") // => 456n

Type guard that narrows an unknown value to bigint.

import { BigInt } from "effect"
BigInt.isBigInt(1n) // => true
BigInt.isBigInt(1) // => false

Order instance for bigints.

import { BigInt } from "effect"
BigInt.Order(123n, 456n) // => -1
BigInt.Order(456n, 123n) // => 1
BigInt.Order(123n, 123n) // => 0

Equivalence instance for bigints, using strict equality.

import { BigInt } from "effect"
BigInt.Equivalence(1n, 1n) // => true
BigInt.Equivalence(1n, 2n) // => false

Adds two bigints (dual).

import { BigInt } from "effect"
BigInt.sum(2n, 3n) // => 5n

Multiplies two bigints (dual).

import { BigInt } from "effect"
BigInt.multiply(2n, 3n) // => 6n

Subtracts the second bigint from the first (dual).

import { BigInt } from "effect"
BigInt.subtract(2n, 3n) // => -1n

Safe division returning Option.none() when the divisor is 0n (dual). Non-exact quotients truncate toward zero.

import { BigInt } from "effect"
BigInt.divide(6n, 3n) // => Option.some(2n)
BigInt.divide(6n, 0n) // => Option.none()

Division that throws when the divisor is 0n (dual). Truncates toward zero.

import { BigInt } from "effect"
BigInt.divideUnsafe(6n, 3n) // => 2n
BigInt.divideUnsafe(6n, 4n) // => 1n (truncated toward zero)

Adds 1n to a bigint.

import { BigInt } from "effect"
BigInt.increment(2n) // => 3n

Subtracts 1n from a bigint.

import { BigInt } from "effect"
BigInt.decrement(3n) // => 2n

Returns the JavaScript % remainder (sign of the dividend), throwing when the divisor is 0n (dual).

import { BigInt } from "effect"
BigInt.remainder(10n, 3n) // => 1n
BigInt.remainder(15n, 4n) // => 3n

Classifies a bigint as negative (-1), zero (0), or positive (1).

import { BigInt } from "effect"
BigInt.sign(-5n) // => -1
BigInt.sign(0n) // => 0
BigInt.sign(5n) // => 1

Returns the absolute value (magnitude with sign removed).

import { BigInt } from "effect"
BigInt.abs(-5n) // => 5n
BigInt.abs(5n) // => 5n

Greatest common divisor of two bigints (dual).

import { BigInt } from "effect"
BigInt.gcd(2n, 3n) // => 1n
BigInt.gcd(16n, 24n) // => 8n

Least common multiple of two bigints (dual).

import { BigInt } from "effect"
BigInt.lcm(2n, 3n) // => 6n
BigInt.lcm(16n, 24n) // => 48n

Safe integer square root returning Option<bigint>; Option.none() for negative input. For non-perfect squares, returns the largest bigint whose square is <= the input.

import { BigInt } from "effect"
BigInt.sqrt(16n) // => Option.some(4n)
BigInt.sqrt(10n) // => Option.some(3n)
BigInt.sqrt(-1n) // => Option.none()

Integer square root that throws a RangeError on negative input. Use when the input is known to be non-negative.

import { BigInt } from "effect"
BigInt.sqrtUnsafe(4n) // => 2n
BigInt.sqrtUnsafe(9n) // => 3n
// BigInt.sqrtUnsafe(-1n) throws RangeError

true if the first argument is strictly less than the second (dual).

import { BigInt } from "effect"
BigInt.isLessThan(2n, 3n) // => true
BigInt.isLessThan(3n, 3n) // => false

true if the first argument is less than or equal to the second (dual).

import { BigInt } from "effect"
BigInt.isLessThanOrEqualTo(3n, 3n) // => true
BigInt.isLessThanOrEqualTo(4n, 3n) // => false

true if the first argument is strictly greater than the second (dual).

import { BigInt } from "effect"
BigInt.isGreaterThan(4n, 3n) // => true
BigInt.isGreaterThan(3n, 3n) // => false

true if the first argument is greater than or equal to the second (dual).

import { BigInt } from "effect"
BigInt.isGreaterThanOrEqualTo(3n, 3n) // => true
BigInt.isGreaterThanOrEqualTo(2n, 3n) // => false

Checks whether a bigint falls inside an inclusive [minimum, maximum] range (dual).

import { BigInt } from "effect"
const between = BigInt.between({ minimum: 0n, maximum: 5n })
between(3n) // => true
between(-1n) // => false
between(6n) // => false

Forces a bigint into an inclusive [minimum, maximum] range (dual).

import { BigInt } from "effect"
const clamp = BigInt.clamp({ minimum: 1n, maximum: 5n })
clamp(3n) // => 3n
clamp(0n) // => 1n
clamp(6n) // => 5n

Returns the smaller of two bigints (dual).

import { BigInt } from "effect"
BigInt.min(2n, 3n) // => 2n

Returns the larger of two bigints (dual).

import { BigInt } from "effect"
BigInt.max(2n, 3n) // => 3n

Converts a bigint to a number safely, returning Option.none() when the value falls outside the safe integer range (guarding against precision loss).

import { BigInt as BI } from "effect"
BI.toNumber(42n) // => Option.some(42)
BI.toNumber(BigInt(Number.MAX_SAFE_INTEGER) + 1n) // => Option.none()

Parses a string into a bigint safely, returning Option.none() for empty or invalid input (rather than throwing like the native constructor).

import { BigInt } from "effect"
BigInt.fromString("42") // => Option.some(42n)
BigInt.fromString(" ") // => Option.none()
BigInt.fromString("a") // => Option.none()

Converts a number to a bigint safely, returning Option.none() for non-integers or values outside the safe integer range.

import { BigInt } from "effect"
BigInt.fromNumber(42) // => Option.some(42n)
BigInt.fromNumber(1.5) // => Option.none() (not an integer)
BigInt.fromNumber(Number.MAX_SAFE_INTEGER + 1) // => Option.none()

Sums an iterable of bigints. Returns 0n for an empty iterable.

import { BigInt } from "effect"
BigInt.sumAll([2n, 3n, 4n]) // => 9n

Multiplies an iterable of bigints. Returns 1n for an empty iterable, and short-circuits to 0n as soon as it sees a 0n.

import { BigInt } from "effect"
BigInt.multiplyAll([2n, 3n, 4n]) // => 24n

Like the Number reducers, these Reducer instances carry an identity value and expose combineAll for folding an iterable (empty iterable returns the identity).

Combines bigints with addition; identity 0n.

import { BigInt } from "effect"
BigInt.ReducerSum.combineAll([10n, 20n, 30n]) // => 60n
BigInt.ReducerSum.combineAll([]) // => 0n

Combines bigints with multiplication; identity 1n. Short-circuits to 0n when it encounters a 0n.

import { BigInt } from "effect"
BigInt.ReducerMultiply.combineAll([2n, 3n, 4n]) // => 24n
BigInt.ReducerMultiply.combineAll([]) // => 1n

A Combiner is the identity-free cousin of a Reducer: it carries only a binary combine operation, so there is no identity value and no combineAll. Use one to merge two values where no neutral element exists (here, max/min).

Returns the larger of two bigints.

import { BigInt } from "effect"
BigInt.CombinerMax.combine(2n, 5n) // => 5n

Returns the smaller of two bigints.

import { BigInt } from "effect"
BigInt.CombinerMin.combine(2n, 5n) // => 2n