Skip to content

Refinement

Refinement: object

Defined in: Core/Refinement.ts:25

and: <A, C>(second) => <B>(first) => Refinement<A, B & C>

Intersects two refinements: the result narrows A to B & C, passing only when both refinements hold simultaneously.

Data-last — the first refinement is the data being piped.

A

C

Refinement<A, C>

<B>(first) => Refinement<A, B & C>

const isString: Refinement<unknown, string> = Refinement.from.predicate(x => typeof x === "string");
const isNonEmpty: Refinement<unknown, { length: number }> =
  Refinement.from.predicate(x => (x as any).length > 0);

const isNonEmptyString = pipe(isString, Refinement.and(isNonEmpty));
isNonEmptyString("hi");  // true
isNonEmptyString("");    // false

compose: <A, B, C>(bc) => (ab) => Refinement<A, C>

Chains two refinements: if ab narrows A to B and bc narrows B to C, the result narrows A directly to C.

Data-last — the first refinement ab is the data being piped.

A

B

C

Refinement<B, C>

(ab) => Refinement<A, C>

type NonEmptyString = string & { readonly _tag: "NonEmpty" };
type TrimmedString  = NonEmptyString & { readonly _tag: "Trimmed" };

const isNonEmpty: Refinement<string, NonEmptyString> =
  Refinement.from.predicate(s => s.length > 0);
const isTrimmed: Refinement<NonEmptyString, TrimmedString> =
  Refinement.from.predicate(s => s === s.trim());

const isNonEmptyTrimmed: Refinement<string, TrimmedString> = pipe(
  isNonEmpty,
  Refinement.compose(isTrimmed)
);

from: object

predicate: <A, B>(f) => Refinement<A, B> = fromPredicate

Creates a Refinement<A, B> from a plain boolean predicate.

This is an unsafe cast — the caller is responsible for ensuring that the predicate truly characterises values of type B. Use this only when bootstrapping a new refinement; prefer compose, and, or or to build derived refinements from existing ones.

A

B

(a) => boolean

Refinement<A, B>

type PositiveNumber = number & { readonly _tag: "PositiveNumber" };

const isPositive: Refinement<number, PositiveNumber> =
  Refinement.from.predicate(n => n > 0);

or: <A, C>(second) => <B>(first) => Refinement<A, C | B>

Unions two refinements: the result narrows A to B | C, passing when either refinement holds.

Data-last — the first refinement is the data being piped.

A

C

Refinement<A, C>

<B>(first) => Refinement<A, C | B>

const isString:  Refinement<unknown, string>  = Refinement.from.predicate(x => typeof x === "string");
const isNumber:  Refinement<unknown, number>  = Refinement.from.predicate(x => typeof x === "number");

const isStringOrNumber = pipe(isString, Refinement.or(isNumber));
isStringOrNumber("hi"); // true
isStringOrNumber(42);   // true
isStringOrNumber(true); // false

to: object

Maybe: <A, B>(r) => (a) => Maybe<B>

Converts a Refinement<A, B> into a function (a: A) => Maybe<B>.

Returns Some(a) when the refinement holds, None otherwise. Useful for integrating runtime validation into a Maybe-based pipeline.

A

B

Refinement<A, B>

(a) => Maybe<B>

type PositiveNumber = number & { readonly _tag: "Positive" };
const isPositive: Refinement<number, PositiveNumber> =
  Refinement.from.predicate(n => n > 0);

pipe(-1, Refinement.to.Maybe(isPositive)); // None
pipe(42, Refinement.to.Maybe(isPositive)); // Some(42)

Result: <A, B, E>(r, onFail) => (a) => Result<E, B>

Converts a Refinement<A, B> into a function (a: A) => Result<E, B>.

Returns Ok(a) when the refinement holds, Err(onFail(a)) otherwise. Use this to surface validation failures as typed errors inside a Result pipeline.

A

B

E

Refinement<A, B>

(a) => E

(a) => Result<E, B>

type NonEmptyString = string & { readonly _tag: "NonEmpty" };
const isNonEmpty: Refinement<string, NonEmptyString> =
  Refinement.from.predicate(s => s.length > 0);

pipe("", Refinement.to.Result(isNonEmpty, () => "must not be empty")); // Err(...)
pipe("hi", Refinement.to.Result(isNonEmpty, () => "must not be empty")); // Ok("hi")