Skip to main content Link Search Menu Expand Document (external link)

SchemaData.ts overview

Base class for value objects that combine Schema.Class with a user-defined equivalence.

Mental model

  • Class is | MEquivalenceBasedEqualityData.Classbuilt on `Schema.Class` instead of `| `MData.Class. Like it, it implements Inspectable.Inspectable, Pipeable.Pipeable and Equal.Equal, and its subclasses provide [MData.idSymbol], [isEquivalentToSymbol], [hasSameTypeMarkerAsSymbol] and [Hash.symbol].
  • A subclass is defined with Class.extend<Self>(identifier)(fields). fields are Schema’s, so the class is itself a Schema that can be decoded, encoded and fed to Arbitrary.schema (property-based testing). No need to write a constructor: it takes the fields as an object.

Common tasks

  • Define a value object: extend Class.extend<Self>(identifier)(fields) and implement the four methods above. Identify the type with a TypeId symbol checked by Predicate.hasProperty in [hasSameTypeMarkerAsSymbol]
  • Decode/encode: Schema.decodeUnknownSync(Point), Schema.encodeSync(Point)
  • Generate instances for property-based testing: Arbitrary.schema(Point)

Gotchas

  • The identifier passed to extend must be unique in the program (it identifies the class in Schema representations).
  • [isEquivalentToSymbol] and [Hash.symbol] must be consistent with each other: instances that are equivalent must have the same hash. Return 0 as hash to disable hashing when the equivalence is cheaper than computing a hash.

Quickstart

Example (Defining, decoding and testing a value object)

import type { Equivalence } from "effect"
import { Arbitrary, Effect, Equal, Hash, Predicate, Schema } from "effect"
import { MData, MEquivalenceBasedEqualityData, MSchemaData } from "@parischap/effect-lib"

const moduleTag = "@parischap/my-app/Person/"
const TypeId: unique symbol = Symbol.for(moduleTag) as TypeId
type TypeId = typeof TypeId

class Type extends MSchemaData.Class.extend<Type>(moduleTag)({
  id: Schema.Finite,
  name: Schema.String
}) {
  [MData.idSymbol](): MData.idSymbolResult {
    return moduleTag
  }

  [Hash.symbol](): number {
    return 0
  }

  [MEquivalenceBasedEqualityData.isEquivalentToSymbol](this: this, that: this): boolean {
    return equivalence(this, that)
  }

  [MEquivalenceBasedEqualityData.hasSameTypeMarkerAsSymbol](that: unknown): boolean {
    return Predicate.hasProperty(that, TypeId)
  }

  protected get [TypeId](): TypeId {
    return TypeId
  }
}

// Two persons are equivalent if they have the same id
const equivalence: Equivalence.Equivalence<Type> = (self, that) => self.id === that.id

const person = Schema.decodeUnknownSync(Type)({ id: 1, name: "Alice" })
console.log(person.toJSON()) // { _id: '@example/Person/', id: 1, name: 'Alice' }
console.log(Equal.equals(person, new Type({ id: 1, name: "Bob" }))) // true
console.log(Schema.encodeSync(Type)(person)) // { id: 1, name: 'Alice' }

// Property-based test: a person is equal to any person with the same id
const result = await Effect.runPromise(
  Arbitrary.checkEffect(Arbitrary.schema(Type), (p) => Equal.equals(p, new Type({ id: p.id, name: "other" })))
)
console.log(result._tag) // 'Passed'

See

  • | MEquivalenceBasedEqualityData`` — same without Schema
  • | MData`` — base class without equality

Exports Grouped by Category


Models

Class (class)

Abstract base class adding Equal.Equal to Schema.Class. Extend it with Class.extend.

Signature

declare class Class

Source

[MData.idSymbol] (method)

Returns the id of this. A string identifier is used as the _id field of the JSON view; a function produces a fully custom representation when invoked with this bound.

Signature

declare const [MData.idSymbol]: () => MData.idSymbolResult

Source

toJSON (method)

Returns the JSON view of this, or its redacted representation if this implements Redactable

Signature

declare const toJSON: () => unknown

Source

[Inspectable.NodeInspectSymbol] (method)

Hooks into Node.js util.inspect.

Signature

declare const [Inspectable.NodeInspectSymbol]: () => unknown

Source

toString (method)

Returns the printable representation of this, or its redacted representation if this implements Redactable

Signature

declare const toString: () => string

Source

[MEquivalenceBasedEqualityData.isEquivalentToSymbol] (method)

Implements the equivalence between this and that. Called only after [hasSameTypeMarkerAsSymbol] confirmed that both share the same type marker.

Signature

declare const [MEquivalenceBasedEqualityData.isEquivalentToSymbol]: (this: this, that: this) => boolean

Source

[MEquivalenceBasedEqualityData.hasSameTypeMarkerAsSymbol] (method)

Returns true when that has the same type marker as this. Not declared as a TypeScript type guard because two instances of the same generic class may share a marker yet differ in their type parameters.

Signature

declare const [MEquivalenceBasedEqualityData.hasSameTypeMarkerAsSymbol]: (that: unknown) => boolean

Source

[Hash.symbol] (method)

Returns the hash of this. Return 0 to disable hashing when computing a real hash would be more expensive than evaluating the equivalence directly.

Signature

declare const [Hash.symbol]: () => number

Source

[Equal.symbol] (method)

Implements Equal.Equal by combining the same-type-marker guard and the equivalence.

Signature

declare const [Equal.symbol]: (this: this, that: Equal.Equal) => boolean

Source

Module markers

moduleTag

Module tag.

Signature

declare const moduleTag: "@parischap/effect-lib/Data/SchemaData/"

Source