A type names a kind of entity and fixes its fields. You declare one at module scope as a class, and that class is both
the runtime schema and the TypeScript type of its instances. Every type carries a DXN with a typename and a version, so
two peers agree on what an object is without sharing code.
makeObject takes the DXN first, then the fields. The optional second argument pipes annotations onto the schema, here
the field that labels an object in lists.
Returns a Type.Type entity — a live, in-memory TypeSchema instance,
NOT a Schema.Schema. Use Type.InstanceType<typeof Foo> for the instance
type and Type.getSchema(Foo) to obtain the underlying Effect Schema.
The entity's id defaults to EntityId.deterministic(typename, version) so
constructing a type never reaches crypto.getRandomValues() — required for
Cloudflare workerd, which forbids RNG calls in global (module-evaluation)
scope. Pass { id } to override (e.g. with EntityId.random() from a
request handler).
Giving the type a nominal class name keeps its inferred type portable for
declaration emit: structural expansion (which can pull in unnameable union
members from referenced schemas) is replaced by a reference to the class.
The second call takes either a schema, or struct fields (wrapped in Schema.Struct) with an optional function
that pipes annotations onto the struct.
Schema for string values. Validates that the input is typeof"string".
@stability ― stable
@category ― models
@since ― 4.0.0
@stability ― stable
@category ― schemas
@since ― 4.0.0
String) },
8
import Annotation
Annotation.
const LabelAnnotation: AnnotationHelper<string[]>
export LabelAnnotation
LabelAnnotation.
AnnotationHelper<string[]>.set: (value: string[]) => <SextendsSchema.Top>(schema: S) => S
Set the annotation on an Effect schema.
Only accepts Schema.Top — annotations must be applied to the
source schema BEFORE wrapping it with Type.makeObject / Type.makeRelation.
In a pipe, place every Annotation.X.set(...) before the Type.make... step.
set(['name']),
9
) {}
10
The last segment of the typename must be camelCase. A typename such as com.example.type.works-at fails to compile.
The JsonSchema module converts a type’s schema to JSON Schema and back. Type.makeObjectFromJsonSchema builds a type
from JSON Schema, for a type that comes from a form, an agent or another system.
1
import*as
import Schema
Schemafrom'effect/Schema';
2
import {
import JsonSchema
JsonSchema,
import Type
Type } from'@dxos/echo';
3
import {
classOrganization
Organization } from'./schema.ts';
4
5
// Out: a JSON Schema document for a form, an agent or another system.
For static Type.Obj / Type.Relation entities the source Effect Schema is
read from a hidden slot — these overloads preserve the instance type.
For Type.Type entities (the meta-schema kind) the schema is rebuilt from
type.jsonSchema; the instance type isn't statically knowable so the wide
AnyEntity overload widens to Schema.Codec<any, any>.
Always call this when you need to interact with the Effect Schema API
(e.g. before passing to Effect.Schema functions). For ECHO-side APIs
(Obj.make, Filter.type, Ref) pass the type entity directly.
Only accepts Type.AnyEntity — raw Schema.Schema values can be used
directly without unwrapping.
@performance ― O(1) cached read; a persisted type rebuilds its Effect Schema from jsonSchema (O(schema size)) on
first access.
Construct a new object-kind type entity from raw metadata — for cases where
an Effect Schema isn't available (e.g. JSON-Schema arriving over the network
or from a UI editor). Parallel to
makeObject
but takes pre-built
jsonSchema instead of piping through an Effect schema.
The returned entity is in-memory; persist it with db.addType(entity).
@performance ― O(schema size); validates the JSON schema into a new in-memory type entity, with no database I/O.
A type stored in a space with db.addType can gain and lose fields after it exists; the change replicates like any
write. The functions throw for a type that is not stored. Objects already made from the type keep their data. Use a new
version and a migration instead when old objects need converting.
Persists a Type definition (clones/forks the entity) so it replicates to other peers.
Runs a conflict query first: if a type with the same typename + version already exists in
this space, the existing persisted entity is returned and no duplicate is created. This is
the only supported way to add Type entities —
add
rejects them.
@performance ― Async; O(t) in the space persisted types, which it queries in full for a duplicate before writing.
Persisted Type.Type entities carry typename in EntityMeta.key (the
canonical registry-provenance field); unnamed drafts fall back to the
entity's object id so the helper always returns a string. Any dxn: or
echo:/ prefix is stripped — typename is a bare identifier, not a URI.
@performance ― O(1) meta read with jsonSchema and id fallbacks.
getTypename(
classPerson
Person); // 'com.example.type.person'
5
const
const version: string
version =
import Type
Type.
constgetVersion: (input: Type.AnyEntity) =>string
Gets the version.
@example0.1.0
@example
0.1.0- (in-database, versioned by automerge heads)
The registry-provenance semver lives in EntityMeta.version; unversioned
drafts default to DRAFT_VERSION ('0.0.0'). In-database entities are
additionally versioned by their automerge heads, which are exposed as the
semver pre-release tag (<semver>-<heads>). In-memory declarations have no
heads and surface the bare semver. Read the registry semver alone via
Type.getMeta(input).version.
@performance ― O(h log h) in Automerge heads for database types (sorted into the suffix per call); O(1) for in-memory
types.
Persisted Type.Type instance (has id) → local EID (echo:///<objectId>).
In-memory Type.Type draft (has id, no typename) → local EID.
When options.prefer === 'named' the result is forced to a DXN — for
persisted type entities the typename + version from meta are used to
construct the DXN; static types already return a DXN by default.
Only accepts Type.AnyEntity entities. Raw Schema.Schema values and the
branded Obj.Unknown / Relation.Unknown schemas are intentionally not
supported — use internal.getSchemaURI or the schema's typename annotation
directly when working at the schema level.
@performance ― O(1); reads the id or schema identifier annotation, allocating a new URI string.
For static Type.Obj / Type.Relation entities the source Effect Schema is
read from a hidden slot — these overloads preserve the instance type.
For Type.Type entities (the meta-schema kind) the schema is rebuilt from
type.jsonSchema; the instance type isn't statically knowable so the wide
AnyEntity overload widens to Schema.Codec<any, any>.
Always call this when you need to interact with the Effect Schema API
(e.g. before passing to Effect.Schema functions). For ECHO-side APIs
(Obj.make, Filter.type, Ref) pass the type entity directly.
Only accepts Type.AnyEntity — raw Schema.Schema values can be used
directly without unwrapping.
@performance ― O(1) cached read; a persisted type rebuilds its Effect Schema from jsonSchema (O(schema size)) on
first access.
Note: Only accepts object schemas / object-kind types, not relation schemas.
Use Relation.make for relations.
@performance ― O(n) in props size; validates props against the schema and allocates a reactive proxy, with no database
I/O.
make(
classPerson
Person, {
name: string
name: 'Ada' });
6
7
import Type
Type.
constisObject: (entity: Type.AnyEntity) => entity is Type.AnyObj
Type guard: narrows a Type.AnyEntity to an object-kind entity. Checks
ENTITIES, not instances — use Obj.isObject for instances. Raw
Schema.Schema values (including the branded Obj.Unknown companion)
are intentionally not accepted; inspect their TypeAnnotation directly.
@performance ― O(1) brand read.
isObject(
classPerson
Person); // true
8
import Type
Type.
constisRelation: (entity: Type.AnyEntity) => entity is Type.AnyRelation
Type guard: narrows a Type.AnyEntity to a relation-kind entity. Checks
ENTITIES, not instances — use Relation.isRelation for instances.
@performance ― O(1) brand read.
isRelation(
classWorksAt
WorksAt); // true
9
import Obj
Obj.
constisObject: (obj: unknown) => obj is Obj.Unknown