<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(
fields: Fields,
derive: {
readonly [K in keyof Fields]: (
s: S["Type"]
) => Option_.Option<Fields[K]["Type"]>
}
): (
self: S
) => decodeTo<
Struct<
Simplify<
{ [K in keyof S["fields"]]: toType<S["fields"][K]> } & Fields
>
>,
S
>Adds derived fields to a struct schema during decoding.
Details
Each new field is derived from the decoded struct value via a function that
returns Option. On encoding the derived fields are stripped. This allows
computed or enriched fields to live in the decoded type without appearing in
the encoded form.
Example (Adding a computed fullName field)
import { Option, Schema } from "effect"
const Person = Schema.Struct({ first: Schema.String, last: Schema.String })
const Extended = Person.pipe(
Schema.extendTo(
{ fullName: Schema.String },
{ fullName: (p) => Option.some(`${p.first} ${p.last}`) }
)
)
const alice = Schema.decodeUnknownSync(Extended)({ first: "Alice", last: "Smith" })
console.log(alice.fullName)
// Alice Smithexport function function extendTo<
S extends Struct<Struct.Fields>,
Fields extends Struct.Fields
>(
fields: Fields,
derive: {
readonly [K in keyof Fields]: (
s: S["Type"]
) => Option_.Option<Fields[K]["Type"]>
}
): (
self: S
) => decodeTo<
Struct<
Simplify<
{
[K in keyof S["fields"]]: toType<
S["fields"][K]
>
} & Fields
>
>,
S
>
Adds derived fields to a struct schema during decoding.
Details
Each new field is derived from the decoded struct value via a function that
returns Option. On encoding the derived fields are stripped. This allows
computed or enriched fields to live in the decoded type without appearing in
the encoded form.
Example (Adding a computed fullName field)
import { Option, Schema } from "effect"
const Person = Schema.Struct({ first: Schema.String, last: Schema.String })
const Extended = Person.pipe(
Schema.extendTo(
{ fullName: Schema.String },
{ fullName: (p) => Option.some(`${p.first} ${p.last}`) }
)
)
const alice = Schema.decodeUnknownSync(Extended)({ first: "Alice", last: "Smith" })
console.log(alice.fullName)
// Alice Smith
extendTo<function (type parameter) S in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>S extends interface Struct<Fields extends Struct.Fields>Defines a struct schema from a map of field schemas.
Details
Each field value is a schema. Use
optionalKey
or
optional
to
mark fields as optional, and
mutableKey
to mark them as mutable.
The resulting schema's Type is a readonly object type with the fields'
decoded types. The Encoded form mirrors the field schemas' encoded types.
Example (Defining a basic struct)
import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }
Namespace for struct field type utilities.
Details
These types compute the decoded Type, encoded Encoded, and constructor
input MakeIn of a
Struct
from its field map, handling optional,
mutable, and other field modifiers automatically.
Struct.Fields — constraint for the field map object
Struct.Type<F> — decoded type of the struct
Struct.Encoded<F> — encoded type of the struct
Struct.MakeIn<F> — constructor input (optional/defaulted fields may be omitted)
Struct.DecodingServices<F> / Struct.EncodingServices<F> — required services
Type-level representation returned by
Struct
.
Struct<Struct.type Struct<Fields extends Struct.Fields>.Fields = {
readonly [x: string]: Constraint;
readonly [x: number]: Constraint;
readonly [x: symbol]: Constraint;
}
Constraint for a struct field map: an object whose values are schemas.
Fields>, const function (type parameter) Fields in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>Fields extends Struct.type Struct<Fields extends Struct.Fields>.Fields = {
readonly [x: string]: Constraint;
readonly [x: number]: Constraint;
readonly [x: symbol]: Constraint;
}
Constraint for a struct field map: an object whose values are schemas.
Fields>(
/** The new fields to add */
fields: const Fields extends Struct.FieldsThe new fields to add
fields: function (type parameter) Fields in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>Fields,
/** A function per field to derive its value from the original input */
derive: {
readonly [K in keyof Fields]: (
s: S["Type"]
) => Option_.Option<Fields[K]["Type"]>
}
A function per field to derive its value from the original input
derive: { readonly [function (type parameter) KK in keyof function (type parameter) Fields in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>Fields]: (s: S["Type"]s: function (type parameter) S in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>S["Type"]) => import Option_Option_.type Option_.Option = /*unresolved*/ anyOption<function (type parameter) Fields in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>Fields[function (type parameter) KK]["Type"]> }
) {
return (
self: S extends Struct<Struct.Fields>self: function (type parameter) S in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>S
): interface decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>Creates a schema that transforms from a source schema to a target schema.
When to use
Use when decoding should change the schema's decoded type or encoded shape,
with an optional custom bidirectional transformation.
Details
Call it with the target schema to and then pipe the source schema from
into the returned function. The resulting schema decodes from
From["Encoded"] to To["Type"] and encodes from To["Type"] back to
From["Encoded"].
When no transformation is provided, SchemaTransformation.passthrough() is
used, so From["Type"] must already be compatible with To["Encoded"].
The resulting schema combines decoding and encoding services from both
schemas and any custom transformation.
Gotchas
In a custom transformation, decode maps From["Type"] to To["Encoded"]
and is used on the encoding path, while encode maps To["Encoded"] to
From["Type"] and is used on the decoding path.
Example (Transforming strings to numbers with a schema transformation)
import { Schema, SchemaGetter } from "effect"
const NumberFromString = Schema.String.pipe(
Schema.decodeTo(
Schema.Number,
{
decode: SchemaGetter.transform((s) => Number(s)),
encode: SchemaGetter.transform((n) => String(n))
}
)
)
const result = Schema.decodeUnknownSync(NumberFromString)("123")
// result: 123
Type-level representation returned by
decodeTo
.
decodeTo<interface Struct<Fields extends Struct.Fields>Defines a struct schema from a map of field schemas.
Details
Each field value is a schema. Use
optionalKey
or
optional
to
mark fields as optional, and
mutableKey
to mark them as mutable.
The resulting schema's Type is a readonly object type with the fields'
decoded types. The Encoded form mirrors the field schemas' encoded types.
Example (Defining a basic struct)
import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }
Namespace for struct field type utilities.
Details
These types compute the decoded Type, encoded Encoded, and constructor
input MakeIn of a
Struct
from its field map, handling optional,
mutable, and other field modifiers automatically.
Struct.Fields — constraint for the field map object
Struct.Type<F> — decoded type of the struct
Struct.Encoded<F> — encoded type of the struct
Struct.MakeIn<F> — constructor input (optional/defaulted fields may be omitted)
Struct.DecodingServices<F> / Struct.EncodingServices<F> — required services
Type-level representation returned by
Struct
.
Struct<type Simplify<T> = { [K in keyof T]: T[K]; }Flattens intersection types into a single object type for readability.
When to use
Use when hovering over a type shows A & B & C instead of the merged shape.
Details
This helper is purely cosmetic at the type level and has no runtime effect.
It preserves readonly modifiers; use
Mutable
to strip them.
Example (Flattening an intersection)
import type { Struct } from "effect"
type Original = { a: string } & { b: number }
// Without Simplify, the type displays as `{ a: string } & { b: number }`
type Simplified = Struct.Simplify<Original>
// { a: string; b: number }
Simplify<{ [function (type parameter) KK in keyof function (type parameter) S in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>S["fields"]]: interface toType<S extends Constraint>Type-level representation returned by
toType
.
Extracts the type-side schema: sets Encoded to equal the decoded Type,
discarding the encoding transformation path.
toType<function (type parameter) S in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>S["fields"][function (type parameter) KK]> } & function (type parameter) Fields in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>Fields>>, function (type parameter) S in extendTo<S extends Struct<Struct.Fields>, const Fields extends Struct.Fields>(fields: Fields, derive: { readonly [K in keyof Fields]: (s: S["Type"]) => Option_.Option<Fields[K]["Type"]>; }): (self: S) => decodeTo<Struct<Simplify<{ [K in keyof S["fields"]]: toType<S["fields"][K]>; } & Fields>>, S>S> => {
const const f: Record<
string,
toType<Constraint>
>
f = import Record_Record_.map(self: S extends Struct<Struct.Fields>self.Struct<Struct.Fields>.fields: Struct.FieldsThe field definitions of this struct. Spread them into a new struct to
reuse fields across schemas.
Example (Reusing fields across structs)
import { Schema } from "effect"
const Timestamped = Schema.Struct({
createdAt: Schema.Date,
updatedAt: Schema.Date
})
const User = Schema.Struct({
...Timestamped.fields,
name: Schema.String,
email: Schema.String
})
fields, const toType: toTypeLambdaType-level representation returned by
toType
.
Extracts the type-side schema: sets Encoded to equal the decoded Type,
discarding the encoding transformation path.
toType)
const const to: Struct<
{
readonly [x: string]: toType<Constraint>
} & Fields
>
const to: {
Type: Struct.Type<Fields>;
Encoded: Struct.Encoded<Fields>;
DecodingServices: Struct.DecodingServices<Fields>;
EncodingServices: Struct.EncodingServices<Fields>;
Iso: Struct.Iso<Fields>;
fields: Fields;
mapFields: (f: (fields: { readonly [x: string]: toType<Constraint> } & Fields) => To, options?: { readonly unsafePreserveChecks?: boolean | undefined } | undefined) => Struct<{ [K in keyof Readonly<To>]: Readonly<To>[K]; }>;
Rebuild: Rebuild;
ast: Ast;
annotate: (annotations: Annotations.Bottom<Struct.View<{ readonly [x: string]: toType<Constraint>; } & Fields, 'Type', Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint>; } & Fields>, Struct.TypeMutableKeys<{ readonly [x: string]: to…;
annotateKey: (annotations: Annotations.Key<Struct.View<{ readonly [x: string]: toType<Constraint> } & Fields, 'Type', Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields>, Struct.TypeMutableKeys<{ readonly [x: string]: toType<…;
check: (checks_0: SchemaAST.Check<Struct.View<{ readonly [x: string]: toType<Constraint> } & Fields, 'Type', Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields>, Struct.TypeMutableKeys<{ readonly [x: string]: toType<Con…;
rebuild: (ast: SchemaAST.Objects) => Struct<{ readonly [x: string]: toType<Constraint> } & Fields>;
make: (input: Struct.MakeInView<{ readonly [x: string]: toType<Constraint> } & Fields, Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields> | Struct.TypeConstructorDefaultedKeys<{ readonly [x: string]: toType<Constraint…;
makeOption: (input: Struct.MakeInView<{ readonly [x: string]: toType<Constraint> } & Fields, Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields> | Struct.TypeConstructorDefaultedKeys<{ readonly [x: string]: toType<Constraint…;
makeEffect: (input: Struct.MakeInView<{ readonly [x: string]: toType<Constraint> } & Fields, Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields> | Struct.TypeConstructorDefaultedKeys<{ readonly [x: string]: toType<Constraint…;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
to = function Struct<
Fields extends Struct.Fields
>(fields: Fields): Struct<Fields>
Defines a struct schema from a map of field schemas.
Details
Each field value is a schema. Use
optionalKey
or
optional
to
mark fields as optional, and
mutableKey
to mark them as mutable.
The resulting schema's Type is a readonly object type with the fields'
decoded types. The Encoded form mirrors the field schemas' encoded types.
Example (Defining a basic struct)
import { Schema } from "effect"
const Person = Schema.Struct({
name: Schema.String,
age: Schema.Number,
email: Schema.optionalKey(Schema.String)
})
// { readonly name: string; readonly age: number; readonly email?: string }
type Person = typeof Person.Type
const alice = Schema.decodeUnknownSync(Person)({ name: "Alice", age: 30 })
console.log(alice)
// { name: 'Alice', age: 30 }
Struct({ ...const f: Record<
string,
toType<Constraint>
>
f, ...fields: const Fields extends Struct.FieldsThe new fields to add
fields })
return self: S extends Struct<Struct.Fields>self.pipe(function decodeTo<Struct<any>, Constraint, never, never>(to: Struct<any>, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<{
readonly [x: number]: any;
[x: symbol]: any;
[x: string]: any;
}>, unknown, never>;
readonly encode: SchemaGetter.Getter<unknown, NoInfer<{
readonly [x: number]: any;
[x: symbol]: any;
[x: string]: any;
}>, never>;
}): (from: Constraint) => decodeTo<Struct<any>, Constraint, never, never> (+1 overload)
Creates a schema that transforms from a source schema to a target schema.
When to use
Use when decoding should change the schema's decoded type or encoded shape,
with an optional custom bidirectional transformation.
Details
Call it with the target schema to and then pipe the source schema from
into the returned function. The resulting schema decodes from
From["Encoded"] to To["Type"] and encodes from To["Type"] back to
From["Encoded"].
When no transformation is provided, SchemaTransformation.passthrough() is
used, so From["Type"] must already be compatible with To["Encoded"].
The resulting schema combines decoding and encoding services from both
schemas and any custom transformation.
Gotchas
In a custom transformation, decode maps From["Type"] to To["Encoded"]
and is used on the encoding path, while encode maps To["Encoded"] to
From["Type"] and is used on the decoding path.
Example (Transforming strings to numbers with a schema transformation)
import { Schema, SchemaGetter } from "effect"
const NumberFromString = Schema.String.pipe(
Schema.decodeTo(
Schema.Number,
{
decode: SchemaGetter.transform((s) => Number(s)),
encode: SchemaGetter.transform((n) => String(n))
}
)
)
const result = Schema.decodeUnknownSync(NumberFromString)("123")
// result: 123
decodeTo(
const to: Struct<
{
readonly [x: string]: toType<Constraint>
} & Fields
>
const to: {
Type: Struct.Type<Fields>;
Encoded: Struct.Encoded<Fields>;
DecodingServices: Struct.DecodingServices<Fields>;
EncodingServices: Struct.EncodingServices<Fields>;
Iso: Struct.Iso<Fields>;
fields: Fields;
mapFields: (f: (fields: { readonly [x: string]: toType<Constraint> } & Fields) => To, options?: { readonly unsafePreserveChecks?: boolean | undefined } | undefined) => Struct<{ [K in keyof Readonly<To>]: Readonly<To>[K]; }>;
Rebuild: Rebuild;
ast: Ast;
annotate: (annotations: Annotations.Bottom<Struct.View<{ readonly [x: string]: toType<Constraint>; } & Fields, 'Type', Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint>; } & Fields>, Struct.TypeMutableKeys<{ readonly [x: string]: to…;
annotateKey: (annotations: Annotations.Key<Struct.View<{ readonly [x: string]: toType<Constraint> } & Fields, 'Type', Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields>, Struct.TypeMutableKeys<{ readonly [x: string]: toType<…;
check: (checks_0: SchemaAST.Check<Struct.View<{ readonly [x: string]: toType<Constraint> } & Fields, 'Type', Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields>, Struct.TypeMutableKeys<{ readonly [x: string]: toType<Con…;
rebuild: (ast: SchemaAST.Objects) => Struct<{ readonly [x: string]: toType<Constraint> } & Fields>;
make: (input: Struct.MakeInView<{ readonly [x: string]: toType<Constraint> } & Fields, Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields> | Struct.TypeConstructorDefaultedKeys<{ readonly [x: string]: toType<Constraint…;
makeOption: (input: Struct.MakeInView<{ readonly [x: string]: toType<Constraint> } & Fields, Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields> | Struct.TypeConstructorDefaultedKeys<{ readonly [x: string]: toType<Constraint…;
makeEffect: (input: Struct.MakeInView<{ readonly [x: string]: toType<Constraint> } & Fields, Struct.TypeOptionalKeys<{ readonly [x: string]: toType<Constraint> } & Fields> | Struct.TypeConstructorDefaultedKeys<{ readonly [x: string]: toType<Constraint…;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
}
to,
import SchemaTransformationSchemaTransformation.function transform<T, E>(options: {
readonly decode: (input: E) => T
readonly encode: (input: T) => E
}): Transformation<T, E>
Creates a Transformation from pure (sync, infallible) decode and encode
functions.
When to use
Use when you need an infallible schema transformation that does not require
Effect services.
Details
- Each function receives the input and returns the output directly.
- Skips
None inputs (missing keys) — functions are only called on present values.
- Does not allocate Effects internally; uses optimized sync path.
Example (Converting between cents and dollars)
import { Schema, SchemaTransformation } from "effect"
const CentsFromDollars = Schema.Number.pipe(
Schema.decodeTo(
Schema.Number,
SchemaTransformation.transform({
decode: (dollars) => dollars * 100,
encode: (cents) => cents / 100
})
)
)
transform({
decode: (input: unknown) => anydecode: (input: unknowninput) => {
const const out: anyout: any = { ...input: unknowninput }
for (const const k: Extract<keyof Fields, string>k in fields: const Fields extends Struct.FieldsThe new fields to add
fields) {
const const f: {
readonly [K in keyof Fields]: (
s: S["Type"]
) => Option_.Option<Fields[K]["Type"]>
}[Extract<keyof Fields, string>]
f = derive: {
readonly [K in keyof Fields]: (
s: S["Type"]
) => Option_.Option<Fields[K]["Type"]>
}
A function per field to derive its value from the original input
derive[const k: Extract<keyof Fields, string>k]
const const o: Option_.Option<
Fields[Extract<keyof Fields, string>]["Type"]
>
o = const f: (
s: S["Type"]
) => Option_.Option<
Fields[Extract<keyof Fields, string>]["Type"]
>
f(input: unknowninput)
if (import Option_Option_.isSome(const o: Option_.Option<
Fields[Extract<keyof Fields, string>]["Type"]
>
o)) {
const out: anyout[const k: Extract<keyof Fields, string>k] = const o: Option_.Some<
Fields[Extract<keyof Fields, string>]["Type"]
>
const o: {
_tag: "Some";
_op: "Some";
value: A;
valueOrUndefined: A;
pipe: { <A>(this: A): A; <A, B = never>(this: A, ab: (_: A) => B): B; <A, B = never, C = never>(this: A, ab: (_: A) => B, bc: (_: B) => C): C; <A, B = never, C = never, D = never>(this: A, ab: (_: A) => B, bc: (_: B) => C, cd: (_: C) => D): D; <…;
toString: () => string;
toJSON: () => unknown;
}
o.value
}
}
return const out: anyout
},
encode: (input: any) => unknownencode: (input: anyinput) => {
const const out: anyout = { ...input: anyinput }
for (const const k: Extract<keyof Fields, string>k in fields: const Fields extends Struct.FieldsThe new fields to add
fields) {
delete const out: anyout[const k: Extract<keyof Fields, string>k]
}
return const out: anyout
}
})
)) as any
}
}