SchemaData.ts overview
Base class for value objects that combine Schema.Class with a user-defined equivalence.
Mental model
Classis|MEquivalenceBasedEqualityData.Classbuilt on `Schema.Class` instead of `| `MData.Class. Like it, it implementsInspectable.Inspectable,Pipeable.PipeableandEqual.Equal, and its subclasses provide[MData.idSymbol],[isEquivalentToSymbol],[hasSameTypeMarkerAsSymbol]and[Hash.symbol].- A subclass is defined with
Class.extend<Self>(identifier)(fields).fieldsareSchema’s, so the class is itself aSchemathat can be decoded, encoded and fed toArbitrary.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 aTypeIdsymbol checked byPredicate.hasPropertyin[hasSameTypeMarkerAsSymbol] - Decode/encode:
Schema.decodeUnknownSync(Point),Schema.encodeSync(Point) - Generate instances for property-based testing:
Arbitrary.schema(Point)
Gotchas
- The identifier passed to
extendmust be unique in the program (it identifies the class inSchemarepresentations). [isEquivalentToSymbol]and[Hash.symbol]must be consistent with each other: instances that are equivalent must have the same hash. Return0as 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 withoutSchema|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
[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
toJSON (method)
Returns the JSON view of this, or its redacted representation if this implements Redactable
Signature
declare const toJSON: () => unknown
[Inspectable.NodeInspectSymbol] (method)
Hooks into Node.js util.inspect.
Signature
declare const [Inspectable.NodeInspectSymbol]: () => unknown
toString (method)
Returns the printable representation of this, or its redacted representation if this implements Redactable
Signature
declare const toString: () => string
[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
[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
[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
[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
Module markers
moduleTag
Module tag.
Signature
declare const moduleTag: "@parischap/effect-lib/Data/SchemaData/"