/** * Comparable is a structure that has an idea of comparability. Comparability * means that two of the same type of object can be compared such that the * condition of comparison can be true or false. The canonical comparison is * equality. * * @module Comparable * @since 2.0.0 */ import "./_dnt.polyfills.js"; import type { Hold, In, Kind, Out, Spread } from "./kind.js"; import type { NonEmptyArray } from "./array.js"; import type { ReadonlyRecord } from "./record.js"; import type { Literal, Schemable } from "./schemable.js"; /** * The compare function in a Comparable. * * @since 2.0.0 */ export type Compare = (second: A) => (first: A) => boolean; /** * @since 2.0.0 */ export type TypeOf = U extends Comparable ? A : never; /** * A Comparable is an algebra with a notion of equality. Specifically, * a Comparable for a type T has an equal method that determines if the * two objects are the same. Comparables can be combined, like many * algebraic structures. The combinators for Comparable in fun can be found * in [comparable.ts](./comparable.ts). * * An instance of a Comparable must obey the following laws: * * 1. Reflexivity: compare(a, a) === true * 2. Symmetry: compare(a, b) === compare(b, a) * 3. Transitivity: if compare(a, b) and compare(b, c), then compare(a, c) * * @since 2.0.0 */ export interface Comparable extends Hold { readonly compare: Compare; } /** * Specifies Comparable as a Higher Kinded Type, with * covariant parameter A corresponding to the 0th * index of any Substitutions. * * @since 2.0.0 */ export interface KindComparable extends Kind { readonly kind: Comparable>; } /** * Specifies Comparable as a Higher Kinded Type, with * contravariant parameter D corresponding to the 0th * index of any Substitutions. * * @since 2.0.0 */ export interface KindContraComparable extends Kind { readonly kind: Comparable>; } /** * Create a Comparable from a Compare function. * * @example * ```ts * import { fromCompare } from "./comparable.ts"; * import { pipe } from "./fn.ts"; * * const { compare } = fromCompare( * (second) => (first) => first === second * ); * * const result = compare(1)(1); // true * ``` * * @since 2.0.0 */ export declare function fromCompare(compare?: Compare): Comparable; /** * Create a Comparable that casts the inner type of another Comparable to * Readonly. * * @example * ```ts * import { readonly, fromCompare } from "./comparable.ts"; * * // This has type Comparable> * const ComparableMutableArray = fromCompare>( * (second) => (first) => first.length === second.length * && first.every((value, index) => value === second[index]) * ); * * // This has type Comparable>> * const ComparableReadonlyArray = readonly(ComparableMutableArray); * ``` * * @since 2.0.0 */ export declare function readonly(comparable: Comparable): Comparable>; /** * A Comparable that can compare any unknown values (and thus can * compare any values). Underneath it uses strict equality * for the comparison. * * @example * ```ts * import { unknown } from "./comparable.ts"; * * const result1 = unknown.compare(1)("Hello"); // false * const result2 = unknown.compare(1)(1); // true * ``` * * @since 2.0.0 */ export declare const unknown: Comparable; /** * A Comparable that compares strings using strict equality. * * @example * ```ts * import { string } from "./comparable.ts"; * * const result1 = string.compare("World")("Hello"); // false * const result2 = string.compare("")(""); // true * ``` * * @since 2.0.0 */ export declare const string: Comparable; /** * A Comparable that compares number using strict equality. * * @example * ```ts * import { number } from "./comparable.ts"; * * const result1 = number.compare(1)(2); // false * const result2 = number.compare(1)(1); // true * ``` * * @since 2.0.0 */ export declare const number: Comparable; /** * A Comparable that compares booleans using strict equality. * * @example * ```ts * import { boolean } from "./comparable.ts"; * * const result1 = boolean.compare(true)(false); // false * const result2 = boolean.compare(true)(true); // true * ``` * * @since 2.0.0 */ export declare const boolean: Comparable; /** * Creates a Comparable that compares a union of literals * using strict equality. * * @example * ```ts * import { literal } from "./comparable.ts"; * * const { compare } = literal(1, 2, "Three"); * * const result1 = compare(1)("Three"); // false * const result2 = compare("Three")("Three"); // true * ``` * * @since 2.0.0 */ export declare function literal>(..._: A): Comparable; /** * Creates a derivative Comparable that can also compare null * values in addition to the source eq. * * @example * ```ts * import { nullable, number } from "./comparable.ts"; * * const { compare } = nullable(number); * * const result1 = compare(1)(null); // false * const result2 = compare(null)(null); // true * ``` * * @since 2.0.0 */ export declare function nullable({ compare }: Comparable): Comparable; /** * Creates a derivative Comparable that can also compare undefined * values in addition to the source eq. * * @example * ```ts * import { undefinable, number } from "./comparable.ts"; * * const { compare } = undefinable(number); * * const result1 = compare(1)(undefined); // false * const result2 = compare(undefined)(undefined); // true * ``` * * @since 2.0.0 */ export declare function undefinable({ compare }: Comparable): Comparable; /** * Creates a Comparable that compares readonly records with items * that have the type compared in the supplied eq. * * @example * ```ts * import { record, number } from "./comparable.ts"; * * const { compare } = record(number); * * const result1 = compare({ one: 1 })({ one: 2 }); // false * const result2 = compare({ one: 1 })({ one: 1 }); // true * ``` * * @since 2.0.0 */ export declare function record(eq: Comparable): Comparable>; /** * Creates a Comparable that compares readonly array with items * that have the type compared in the supplied eq. * * @example * ```ts * import { array, number } from "./comparable.ts"; * * const { compare } = array(number); * * const result1 = compare([1, 2])([1, 2, 3]); // false * const result2 = compare([1, 2])([1, 2]); // true * ``` * * @since 2.0.0 */ export declare function array({ compare }: Comparable): Comparable>; /** * Creates a eq that compares, index for index, tuples according * to the order and eqs passed into tuple. * * @example * ```ts * import { tuple, number, string } from "./comparable.ts"; * * const { compare } = tuple(number, string); * * const result1 = compare([1, "Hello"])([1, "Goodbye"]); // false * const result2 = compare([1, ""])([1, ""]); // true * ``` * * @since 2.0.0 */ export declare function tuple>>(...comparables: T): Comparable<{ [K in keyof T]: T[K] extends Comparable ? A : never; }>; /** * Create a eq that compares, key for key, structs according * to the structure of the eqs passed into struct. * * @example * ```ts * import { struct, number, string } from "./comparable.ts"; * * const { compare } = struct({ name: string, age: number }); * * const brandon = { name: "Brandon", age: 37 }; * const emily = { name: "Emily", age: 32 }; * * const result1 = compare(brandon)(emily); // false * const result2 = compare(brandon)(brandon); // true * ``` * * @since 2.0.0 */ export declare function struct(comparables: { readonly [K in keyof A]: Comparable; }): Comparable<{ readonly [K in keyof A]: A[K]; }>; /** * Create a eq that compares, key for key, structs according * to the structure of the eqs passed into struct. It allows * the values in the struct to be optional or null. * * @example * ```ts * import { struct, number, string } from "./comparable.ts"; * * const { compare } = struct({ name: string, age: number }); * * const brandon = { name: "Brandon", age: 37 }; * const emily = { name: "Emily", age: 32 }; * * const result1 = compare(brandon)(emily); // false * const result2 = compare(brandon)(brandon); // true * ``` * * @since 2.0.0 */ export declare function partial(comparables: { readonly [K in keyof A]: Comparable; }): Comparable<{ readonly [K in keyof A]?: A[K]; }>; /** * Create a eq from two other eqs. The resultant eq checks * that any two values are equal according to both supplied eqs. * * @example * ```ts * import { intersect, struct, partial, string } from "./comparable.ts"; * import { pipe } from "./fn.ts"; * * const { compare } = pipe( * struct({ firstName: string }), * intersect(partial({ lastName: string })) * ); * * const batman = { firstName: "Batman" }; * const grace = { firstName: "Grace", lastName: "Hopper" }; * * const result1 = compare(batman)(grace); // false * const result2 = compare(grace)(grace); // true * ``` * * @since 2.0.0 */ export declare function intersect(second: Comparable): (first: Comparable) => Comparable>; /** * Create a Comparable from two other Comparables. The resultant Comparable checks * that any two values are equal according to at least one of the supplied * eqs. * * It should be noted that we cannot differentiate the eq used to * compare two disparate types like number and number[]. Thus, internally * union must type cast to any and treat thrown errors as a false * equivalence. * * @example * ```ts * import { union, number, string } from "./comparable.ts"; * import { pipe } from "./fn.ts"; * * const { compare } = pipe(number, union(string)); * * const result1 = compare(1)("Hello"); // false * const result2 = compare(1)(1); // true * ``` * * @since 2.0.0 */ export declare function union(second: Comparable): (first: Comparable) => Comparable; /** * Create a eq that evaluates lazily. This is useful for equality * of recursive types (either mutual or otherwise). * * @example * ```ts * import { lazy, intersect, struct, partial, string, Comparable } from "./comparable.ts"; * import { pipe } from "./fn.ts"; * * type Person = { name: string, child?: Person }; * * // Annotating the type is required for recursion * const person: Comparable = lazy('Person', () => pipe( * struct({ name: string }), * intersect(partial({ child: person })) * )); * * const icarus = { name: "Icarus" }; * const daedalus = { name: "Daedalus", child: icarus }; * * const result1 = person.compare(icarus)(daedalus); // false * const result2 = person.compare(daedalus)(daedalus); // true * ``` * * @since 2.0.0 */ export declare function lazy(_: string, f: () => Comparable): Comparable; /** * Create a eq that tests the output of a thunk (IO). This assumes that * the output of the thunk is always the same, which for true IO is not * the case. This assumes that the context for the function is undefined, * which means that it doesn't rely on "this" to execute. * * @example * ```ts * import { struct, thunk, number } from "./comparable.ts"; * * const { compare } = struct({ asNumber: thunk(number) }); * * const one = { asNumber: () => 1 }; * const two = { asNumber: () => 2 }; * * const result1 = compare(one)(two); // false * const result2 = compare(one)(one); // true * ``` * * @since 2.0.0 */ export declare function thunk({ compare }: Comparable): Comparable<() => A>; /** * Create a eq from a method on a class or prototypical object. This * exists because many objects in javascript do now allow you to pass an * object method around on its own without its parent object. For example, * if you pass Date.valueOf (type () => number) into another function and * call it, the call will fail because valueOf does not carry the reference * of its parent object around. * * @example * ```ts * import { method, number } from "./comparable.ts"; * * // This eq will work for date, but also for any objects that have * // a valueOf method that returns a number. * const date = method("valueOf", number); * * const now = new Date(); * const alsoNow = new Date(Date.now()); * const later = new Date(Date.now() + 60 * 60 * 1000); * * const result1 = date.compare(now)(alsoNow); // true * const result2 = date.compare(now)(later); // false * ``` * * @since 2.0.0 */ export declare function method(method: M, { compare }: Comparable): Comparable<{ readonly [K in M]: () => A; }>; /** * Create a Comparable using a Comparable and a function that takes * a type L and returns a type D. * * @example * ```ts * import { premap, number } from "./comparable.ts"; * import { pipe } from "./fn.ts"; * * const dateToNumber = (d: Date): number => d.valueOf(); * const date = pipe(number, premap(dateToNumber)); * * const now = new Date(); * const alsoNow = new Date(Date.now()); * const later = new Date(Date.now() + 60 * 60 * 1000); * * const result1 = date.compare(now)(alsoNow); // true * const result2 = date.compare(now)(later); // false * ``` * * Another use for premap with eq is to check for * equality after normalizing data. In the following we * can compare strings ignoring case by normalizing to * lowercase strings. * * @example * ```ts * import { premap, string } from "./comparable.ts"; * import { pipe } from "./fn.ts"; * * const lowercase = (s: string) => s.toLowerCase(); * const insensitive = pipe( * string, // exact string compare * premap(lowercase), // makes all strings lowercase * ); * * const result1 = insensitive.compare("Hello")("World"); // false * const result2 = insensitive.compare("hello")("Hello"); // true * ``` * * @since 2.0.0 */ export declare function premap(fld: (l: L) => D): (eq: Comparable) => Comparable; /** * The canonical implementation of Schemable for a Comparable. It contains * the methods unknown, string, number, boolean, literal, nullable, * undefinable, record, array, tuple, struct, partial, intersect, * union, and lazy. * * @since 2.0.0 */ export declare const SchemableComparable: Schemable;