Skip to content

Standard Library

Effect ships a small “standard library” of helpers for the JavaScript primitives you already use every day. Each primitive type gets its own module (String, Number, BigInt, Boolean), plus two modules for working with functions and conditions (Function, Predicate). They are imported from the core effect package:

import { String, Number, Boolean, BigInt, Predicate, Function } from "effect"

These modules do not replace the native globals — they wrap and extend them. Every module re-exports the global constructor under its own name, so String.String === globalThis.String and Number.Number === globalThis.Number. On top of that, they add data-last, pipeable, curried helpers designed to drop into pipe / flow, and Option-returning safe variants for the many native methods that signal failure with sentinels like -1, undefined, or null.

import { String, Option, pipe } from "effect"
// Native sentinel: indexOf returns -1 when not found
"hello".indexOf("z") // => -1
// Effect: an explicit Option instead
pipe("hello", String.indexOf("z")) // => Option.none()
pipe("hello", String.indexOf("l")) // => Option.some(2)

String, Number, Boolean, and BigInt collide with TypeScript’s global types and constructors. The Effect convention is to import the namespace and reference members through it (String.trim, Number.sum), rather than destructuring individual functions. This keeps call sites readable and avoids shadowing the globals you still want available.

import { Array, String } from "effect"
// `Array` and `String` here are the Effect namespaces; the globals are still
// reachable as `Array.from` / `String.fromCharCode` via the re-exported ctor.
const slug = pipe("User Profile ID", String.kebabCase) // => "user-profile-id"
const parts = Array.map(["a", "b"], String.toUpperCase) // => ["A", "B"]

A handful of patterns recur in every module here. Learn them once and they apply throughout.

  • Dual (data-first / data-last) signatures. Most combinators can be called directly (Number.sum(2, 3)) or partially applied for pipe (pipe(2, Number.sum(3))). See Dual APIs.
  • Option-returning safe variants vs *Unsafe throwing variants. Operations that can fail return an Option (Number.divide, String.at, BigInt.sqrt). Where a throwing counterpart exists, it is named with an Unsafe suffix (Number.divideUnsafe).
  • Built-in Order and Equivalence instances. Each primitive module exports Order and Equivalence so the type plugs into generic sorting and comparison APIs. See Order and Equivalence.
  • Reducer / Combiner instances for folding. Modules expose named reducers (Number.ReducerSum, String.ReducerConcat, Boolean.ReducerAnd) for use with APIs that fold a collection through a Reducer or Combiner.

The helpers you reach for most often, across all of these modules. Each module has its own page (linked below) with the complete reference; this is the shortlist.

import { pipe, flow, String } from "effect"
// Thread a value through steps, left to right
pipe(" Hello World ", String.trim, String.toLowerCase) // => "hello world"
// `flow` builds a reusable point-free function from the same steps
const shout = flow(String.trim, String.toUpperCase)
shout(" hi ") // => "HI"

Parse and convert safely — get an Option, never NaN

Section titled “Parse and convert safely — get an Option, never NaN”
import { Number, BigInt } from "effect"
Number.parse("3.14") // => Option.some(3.14)
Number.parse("nope") // => Option.none()
BigInt.fromString("42") // => Option.some(42n)
BigInt.fromNumber(1.5) // => Option.none() (not an integer)

Arithmetic and ranges that don’t surprise you

Section titled “Arithmetic and ranges that don’t surprise you”
import { Number } from "effect"
Number.divide(6, 0) // => Option.none() (no Infinity)
Number.clamp(15, { minimum: 0, maximum: 10 }) // => 10
Number.between(5, { minimum: 0, maximum: 10 }) // => true
Number.sumAll([1, 2, 3]) // => 6

Reshape strings and convert identifier case

Section titled “Reshape strings and convert identifier case”
import { String, pipe } from "effect"
String.snakeCase("User Profile ID") // => "user_profile_id"
String.kebabCase("User Profile ID") // => "user-profile-id"
String.camelCase("user profile id") // => "userProfileId"
pipe("a,b,c", String.split(",")) // => ["a", "b", "c"]
pipe("hello", String.indexOf("l")) // => Option.some(2) (not -1)

Compose type guards — and keep the narrowing

Section titled “Compose type guards — and keep the narrowing”
import { Predicate } from "effect"
const isPositive = (n: number) => n > 0
// compose: narrow unknown -> number, then refine further
const isPosNum = Predicate.compose(Predicate.isNumber, isPositive)
isPosNum(5) // => true
isPosNum("x") // => false
// and / or / not over predicates of the same input
const isEven = (n: number) => n % 2 === 0
Predicate.and(isPositive, isEven)(4) // => true
import { Boolean } from "effect"
Boolean.match(true, {
onTrue: () => "go",
onFalse: () => "wait"
}) // => "go"
  • Function utilitiespipe, flow, dual, compose, identity, constant, flip, tupled, memoize, and the absurd / hole escape hatches.
  • String — trimming, safe character/substring access, searching, splitting, line iteration, and identifier case conversion.
  • Number & BigInt — safe arithmetic, comparison predicates, clamping, parsing, rounding, gcd/lcm, and fold reducers.
  • Predicates & BooleansPredicate type guards and combinators, plus Boolean algebra and Boolean.match.