/** * Eq Instance for some common scenarios including deep equality. * * @since 0.9.2 */ import fde from 'fast-deep-equal' import * as B from 'fp-ts/boolean' import * as D from 'fp-ts/Date' import * as Eq from 'fp-ts/Eq' import * as N from 'fp-ts/number' import * as RA from 'fp-ts/ReadonlyArray' import * as RR from 'fp-ts/ReadonlyRecord' import * as S from 'fp-ts/string' import { constant, constFalse, constTrue } from './function' import { memoize } from './internal' import { Schemable1 } from './Schemable' /** * @since 0.9.2 * @category Instance */ export const alwaysEqualsEq: Eq.Eq = { equals: constant(constTrue) } /** * @since 0.9.2 * @category Instance */ export const neverEqualsEq: Eq.Eq = { equals: constant(constFalse) } /** * A deep-equality Eq instance. * Supports Reference equality, all JavaScript Primitives including `RegExp`, `Set` and `Map`. * @since 0.9.2 * @category Instance */ export const deepEqualsEq: Eq.Eq = Eq.fromEquals((b) => (a) => fde(a, b)) // ------------------------------------------------------------------------------------- // primitives // ------------------------------------------------------------------------------------- /** * @category primitives * @since 0.9.5 */ export const string: Eq.Eq = S.Eq /** * @category primitives * @since 0.9.5 */ export const number: Eq.Eq = N.Eq /** * @category primitives * @since 0.9.5 */ export const boolean: Eq.Eq = B.Eq /** * @category primitives * @since 0.9.5 */ export const unknownArray: Eq.Eq> = Eq.fromEquals( (second) => (first) => first.length === second.length, ) /** * @category primitives * @since 0.9.5 */ export const unknownRecord: Eq.Eq>> = Eq.fromEquals( (second) => (first) => { for (const k in first) { if (!(k in second)) { return false } } for (const k in second) { if (!(k in first)) { return false } } return true }, ) // ------------------------------------------------------------------------------------- // Combinator // ------------------------------------------------------------------------------------- /** * @category Combinator * @since 0.9.5 */ export const nullable = (or: Eq.Eq): Eq.Eq => Eq.fromEquals( (second) => (first) => first === null || second === null ? first === second : or.equals(second)(first), ) /** * @category Combinator * @since 0.9.5 */ export const optional = (or: Eq.Eq): Eq.Eq => Eq.fromEquals( (second) => (first) => first === undefined || second === undefined ? first === second : or.equals(second)(first), ) /** * @category Combinator * @since 0.9.5 */ export const tuple: >( ...components: { [K in keyof A]: Eq.Eq } ) => Eq.Eq = Eq.tuple /** * @category Combinator * @since 2.2.15 */ export const struct: ( properties: { [K in keyof A]: Eq.Eq }, ) => Eq.Eq<{ [K in keyof A]: A[K] }> = Eq.struct /** * @category Combinator * @since 0.9.5 */ export const partial = ( properties: { [K in keyof A]: Eq.Eq }, ): Eq.Eq> => Eq.fromEquals((second) => (first) => { for (const k in properties) { const xk = first[k] const yk = second[k] if (!(xk === undefined || yk === undefined ? xk === yk : properties[k].equals(xk!)(yk!))) { return false } } return true }) /** * @category Combinator * @since 0.9.5 */ export const array: (item: Eq.Eq) => Eq.Eq> = RA.getEq /** * @category Combinator * @since 0.9.5 */ export const record: (codomain: Eq.Eq) => Eq.Eq> = RR.getEq /** * @category Combinator * @since 0.9.5 */ export const intersect = (right: Eq.Eq) => (left: Eq.Eq): Eq.Eq => Eq.fromEquals((second) => (first) => left.equals(second)(first) && right.equals(second)(first)) /** * @category Combinator * @since 0.9.5 */ export function lazy(f: () => Eq.Eq): Eq.Eq { const get = memoize>(f) return { equals: (second) => (first) => get().equals(second)(first), } } /** * @category Combinator * @since 0.9.5 */ export const sum = ( tag: T, ): ((members: { [K in keyof A]: Eq.Eq> }) => Eq.Eq) => { return (members: Record>) => Eq.fromEquals((second: Record) => (first: Record) => { const ftag = first[tag] return ftag === second[tag] && members[ftag].equals(second)(first) }) } declare module 'fp-ts/HKT' { interface URItoKind { readonly '@typed/fp/ToEq': Eq.Eq } } /** * @category Instance * @since 0.9.4 */ export const Schemable: Schemable1<'@typed/fp/ToEq'> = { URI: '@typed/fp/ToEq', string, number, boolean, date: D.Eq, literal: () => Eq.EqStrict, tuple, struct, array, record, nullable, intersect, lazy: (_, f) => lazy(f), sum, branded: (e) => e as any, unknownArray, unknownRecord, } export * from 'fp-ts/Eq'