<R extends Record<string, Equivalence<any>>>(fields: R): Equivalence<{
readonly [K in keyof R]: [R[K]] extends [Equivalence<infer A>]
? A
: never
}>Creates an equivalence for objects by comparing their properties using provided equivalences.
When to use
Use when you need an Equivalence for objects with known, fixed property
names.
Details
Compares only the properties specified in the struct definition; other
properties are ignored. String and symbol keys are supported via
Reflect.ownKeys. The result returns true only if all specified properties
are equivalent according to their equivalences, and it also satisfies
reflexive, symmetric, and transitive properties.
Example (Comparing structs with different equivalences per field)
import { Equivalence } from "effect"
interface Person {
name: string
age: number
email: string
}
const caseInsensitive = Equivalence.mapInput(
Equivalence.strictEqual<string>(),
(s: string) => s.toLowerCase()
)
const personEq = Equivalence.Struct({
name: caseInsensitive,
age: Equivalence.strictEqual<number>(),
email: caseInsensitive
})
const person1 = { name: "Alice", age: 30, email: "[email protected]" }
const person2 = { name: "ALICE", age: 30, email: "[email protected]" }
const person3 = { name: "Alice", age: 31, email: "[email protected]" }
console.log(personEq(person1, person2)) // true (case-insensitive match)
console.log(personEq(person1, person3)) // false (different age)Example (Comparing specific fields)
import { Equivalence } from "effect"
const nameAgeEq = Equivalence.Struct({
name: Equivalence.strictEqual<string>(),
age: Equivalence.strictEqual<number>()
})
// Only compares name and age, ignores other properties
const obj1 = { name: "Alice", age: 30, extra: "ignored" }
const obj2 = { name: "Alice", age: 30, extra: "different" }
console.log(nameAgeEq(obj1, obj2)) // trueexport function function Struct<
R extends Record<string, Equivalence<any>>
>(
fields: R
): Equivalence<{
readonly [K in keyof R]: [R[K]] extends [
Equivalence<infer A>
]
? A
: never
}>
Creates an equivalence for objects by comparing their properties using provided equivalences.
When to use
Use when you need an Equivalence for objects with known, fixed property
names.
Details
Compares only the properties specified in the struct definition; other
properties are ignored. String and symbol keys are supported via
Reflect.ownKeys. The result returns true only if all specified properties
are equivalent according to their equivalences, and it also satisfies
reflexive, symmetric, and transitive properties.
Example (Comparing structs with different equivalences per field)
import { Equivalence } from "effect"
interface Person {
name: string
age: number
email: string
}
const caseInsensitive = Equivalence.mapInput(
Equivalence.strictEqual<string>(),
(s: string) => s.toLowerCase()
)
const personEq = Equivalence.Struct({
name: caseInsensitive,
age: Equivalence.strictEqual<number>(),
email: caseInsensitive
})
const person1 = { name: "Alice", age: 30, email: "[email protected]" }
const person2 = { name: "ALICE", age: 30, email: "[email protected]" }
const person3 = { name: "Alice", age: 31, email: "[email protected]" }
console.log(personEq(person1, person2)) // true (case-insensitive match)
console.log(personEq(person1, person3)) // false (different age)
Example (Comparing specific fields)
import { Equivalence } from "effect"
const nameAgeEq = Equivalence.Struct({
name: Equivalence.strictEqual<string>(),
age: Equivalence.strictEqual<number>()
})
// Only compares name and age, ignores other properties
const obj1 = { name: "Alice", age: 30, extra: "ignored" }
const obj2 = { name: "Alice", age: 30, extra: "different" }
console.log(nameAgeEq(obj1, obj2)) // true
Struct<function (type parameter) R in Struct<R extends Record<string, Equivalence<any>>>(fields: R): Equivalence<{ readonly [K in keyof R]: [R[K]] extends [Equivalence<infer A>] ? A : never; }>R extends type Record<K extends keyof any, T> = {
[P in K]: T
}
Construct a type with a set of properties K of type T
Record<string, type Equivalence<in A> = (
self: A,
that: A
) => boolean
Represents an equivalence relation over type A.
When to use
Use as a type annotation when you accept or return an equivalence function.
Details
- Returns
boolean: true if values are equivalent, false otherwise
- Must satisfy reflexive, symmetric, and transitive properties
Example (Defining simple number equivalence)
import type { Equivalence } from "effect"
const numberEq: Equivalence.Equivalence<number> = (a, b) => a === b
console.log(numberEq(1, 1)) // true
console.log(numberEq(1, 2)) // false
Example (Defining custom object equivalence)
import type { Equivalence } from "effect"
interface Point {
x: number
y: number
}
const pointEq: Equivalence.Equivalence<Point> = (a, b) =>
a.x === b.x && a.y === b.y
console.log(pointEq({ x: 1, y: 2 }, { x: 1, y: 2 })) // true
Equivalence<any>>>(
fields: R extends Record<string, Equivalence<any>>fields: function (type parameter) R in Struct<R extends Record<string, Equivalence<any>>>(fields: R): Equivalence<{ readonly [K in keyof R]: [R[K]] extends [Equivalence<infer A>] ? A : never; }>R
): type Equivalence<in A> = (
self: A,
that: A
) => boolean
Represents an equivalence relation over type A.
When to use
Use as a type annotation when you accept or return an equivalence function.
Details
- Returns
boolean: true if values are equivalent, false otherwise
- Must satisfy reflexive, symmetric, and transitive properties
Example (Defining simple number equivalence)
import type { Equivalence } from "effect"
const numberEq: Equivalence.Equivalence<number> = (a, b) => a === b
console.log(numberEq(1, 1)) // true
console.log(numberEq(1, 2)) // false
Example (Defining custom object equivalence)
import type { Equivalence } from "effect"
interface Point {
x: number
y: number
}
const pointEq: Equivalence.Equivalence<Point> = (a, b) =>
a.x === b.x && a.y === b.y
console.log(pointEq({ x: 1, y: 2 }, { x: 1, y: 2 })) // true
Equivalence<{ readonly [function (type parameter) KK in keyof function (type parameter) R in Struct<R extends Record<string, Equivalence<any>>>(fields: R): Equivalence<{ readonly [K in keyof R]: [R[K]] extends [Equivalence<infer A>] ? A : never; }>R]: [function (type parameter) R in Struct<R extends Record<string, Equivalence<any>>>(fields: R): Equivalence<{ readonly [K in keyof R]: [R[K]] extends [Equivalence<infer A>] ? A : never; }>R[function (type parameter) KK]] extends [type Equivalence<in A> = (
self: A,
that: A
) => boolean
Represents an equivalence relation over type A.
When to use
Use as a type annotation when you accept or return an equivalence function.
Details
- Returns
boolean: true if values are equivalent, false otherwise
- Must satisfy reflexive, symmetric, and transitive properties
Example (Defining simple number equivalence)
import type { Equivalence } from "effect"
const numberEq: Equivalence.Equivalence<number> = (a, b) => a === b
console.log(numberEq(1, 1)) // true
console.log(numberEq(1, 2)) // false
Example (Defining custom object equivalence)
import type { Equivalence } from "effect"
interface Point {
x: number
y: number
}
const pointEq: Equivalence.Equivalence<Point> = (a, b) =>
a.x === b.x && a.y === b.y
console.log(pointEq({ x: 1, y: 2 }, { x: 1, y: 2 })) // true
Equivalence<infer function (type parameter) AA>] ? function (type parameter) AA : never }> {
const const keys: any[]keys: interface Array<T>Array<any> = Reflect.function Reflect.ownKeys(target: object): (string | symbol)[]Returns the string and symbol keys of the own properties of an object. The own properties of an object
are those that are defined directly on that object, and are not inherited from the object's prototype.
ownKeys(fields: R extends Record<string, Equivalence<any>>fields)
return const make: <A>(
isEquivalent: (self: A, that: A) => boolean
) => Equivalence<A>
Creates a custom equivalence relation with an optimized reference equality check.
When to use
Use when you need an equality rule that the built-in instances and input
mapping helpers cannot express, and you can provide a law-abiding comparison.
Details
The returned equivalence first checks reference equality (===) for
performance. If the values are not the same reference, it falls back to the
provided equivalence function, which must satisfy reflexive, symmetric, and
transitive properties.
Example (Case-insensitive string equivalence)
import { Equivalence } from "effect"
const caseInsensitive = Equivalence.make<string>((a, b) =>
a.toLowerCase() === b.toLowerCase()
)
console.log(caseInsensitive("Hello", "HELLO")) // true
console.log(caseInsensitive("foo", "bar")) // false
// Same reference optimization
const str = "test"
console.log(caseInsensitive(str, str)) // true (fast path)
Example (Comparing numbers with tolerance)
import { Equivalence } from "effect"
const tolerance = Equivalence.make<number>((a, b) => Math.abs(a - b) < 0.0001)
console.log(tolerance(1.0, 1.001)) // false
console.log(tolerance(1.0, 1.00001)) // true
make((self: {
readonly [K in keyof R]: [R[K]] extends [
Equivalence<infer A>
]
? A
: never
}
self, that: {
readonly [K in keyof R]: [R[K]] extends [
Equivalence<infer A>
]
? A
: never
}
that) => {
for (const const key: anykey of const keys: any[]keys) {
if (!fields: R extends Record<string, Equivalence<any>>fields[const key: anykey](self: {
readonly [K in keyof R]: [R[K]] extends [
Equivalence<infer A>
]
? A
: never
}
self[const key: anykey], that: {
readonly [K in keyof R]: [R[K]] extends [
Equivalence<infer A>
]
? A
: never
}
that[const key: anykey])) return false
}
return true
})
}