Skip to content

Refinement

Refinement: object

Defined in: Core/Refinement.ts:27

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>

type HasId = { readonly id: string };
type HasEmail = { readonly email: string };
const hasId: Refinement<object, HasId> = Refinement.from.Predicate((x): x is HasId => "id" in x);
const hasEmail: Refinement<object, HasEmail> = Refinement.from.Predicate((x): x is HasEmail => "email" in x);

const hasIdAndEmail = pipe(hasId, Refinement.and(hasEmail));

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>(predicate) => 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

(value) => 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>(refinement) => (value) => 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>

(value) => 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)

Predicate: <A, B>(refinement) => Predicate<A>

Converts a Refinement<A, B> into a Predicate<A>, discarding the compile-time narrowing.

This is a zero-cost runtime conversion.

A

B

Refinement<A, B>

Predicate<A>

Predicate.from.Refinement for the inverse converter under the Predicate namespace.

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

const isNonEmptyPredicate = Refinement.to.Predicate(isNonEmpty);
isNonEmptyPredicate("hello"); // true
isNonEmptyPredicate("");      // false

Result: <A, B, E>(refinement, onFail) => (value) => 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>

(value) => E

(value) => 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")