decodeTo<To, From, RD, RE>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: 123export interface 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<function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To extends Constraint, function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>From extends Constraint, function (type parameter) RD in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>RD = never, function (type parameter) RE in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>RE = never> extends
interface BottomLazy<out Ast extends SchemaAST.AST, out Rebuild extends Top, in out TypeParameters extends ReadonlyArray<Constraint> = readonly [], out TypeMutability extends Mutability = "readonly", out TypeOptionality extends Optionality = "required", out TypeConstructorDefault extends ConstructorDefault = "no-default", out EncodedMutability extends Mutability = "readonly", out EncodedOptionality extends Optionality = "required">Lazy Bottom variant for schema implementations that compute their public
views on demand.
When to use
Use as an implementation base for schema interfaces that must expose
Bottom behavior without forcing TypeScript to eagerly evaluate expensive
Type, Encoded, or service views.
Details
The laziness is purely type-level; runtime behavior is unchanged.
BottomLazy keeps the structural operations inherited from Bottom, but
erases the expensive schema views to unknown. Concrete schema interfaces can
then redeclare the precise views they expose. This keeps wide schemas such as
Struct and Union cheaper when generic code reads a single view, while
preserving their exact public types.
BottomLazy<
function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["ast"],
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<function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To, function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>From, function (type parameter) RD in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>RD, function (type parameter) RE in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>RE>,
function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["~type.parameters"],
function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["~type.mutability"],
function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["~type.optionality"],
function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["~type.constructor.default"],
function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>From["~encoded.mutability"],
function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>From["~encoded.optionality"]
>
{
readonly "Type": function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["Type"]
readonly "Encoded": function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>From["Encoded"]
readonly "DecodingServices": function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["DecodingServices"] | function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>From["DecodingServices"] | function (type parameter) RD in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>RD
readonly "EncodingServices": function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["EncodingServices"] | function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>From["EncodingServices"] | function (type parameter) RE in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>RE
readonly "~type.make.in": function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["~type.make.in"]
readonly "~type.make": function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["~type.make"]
readonly "Iso": function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To["Iso"]
readonly decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>.from: From extends Constraintfrom: function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>From
readonly decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>.to: To extends Constraintto: function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>To
}
/**
* Type-level representation returned by {@link decodeTo} without a custom transformation.
*
* @category transforming
* @since 3.10.0
*/
export interface interface compose<To extends Constraint, From extends Constraint>Type-level representation returned by
decodeTo
without a custom transformation.
compose<function (type parameter) To in compose<To extends Constraint, From extends Constraint>To extends Constraint, function (type parameter) From in compose<To extends Constraint, From extends Constraint>From extends Constraint> extends 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<function (type parameter) To in compose<To extends Constraint, From extends Constraint>To, function (type parameter) From in compose<To extends Constraint, From extends Constraint>From> {}
/**
* 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)
*
* ```ts
* 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
* ```
*
* @category transforming
* @since 4.0.0
*/
export function function decodeTo<To extends Constraint>(to: To): <From extends Constraint>(from: From) => compose<To, From> (+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<function (type parameter) To in decodeTo<To extends Constraint>(to: To): <From extends Constraint>(from: From) => compose<To, From>To extends Constraint>(to: To extends Constraintto: function (type parameter) To in decodeTo<To extends Constraint>(to: To): <From extends Constraint>(from: From) => compose<To, From>To): <function (type parameter) From in <From extends Constraint>(from: From): compose<To, From>From extends Constraint>(from: From extends Constraintfrom: function (type parameter) From in <From extends Constraint>(from: From): compose<To, From>From) => interface compose<To extends Constraint, From extends Constraint>Type-level representation returned by
decodeTo
without a custom transformation.
compose<function (type parameter) To in decodeTo<To extends Constraint>(to: To): <From extends Constraint>(from: From) => compose<To, From>To, function (type parameter) From in <From extends Constraint>(from: From): compose<To, From>From>
export function function decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE> (+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<function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
To extends Constraint, function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
From extends Constraint, function (type parameter) RD in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
RD = never, function (type parameter) RE in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
RE = never>(
to: To extends Constraintto: function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
To,
transformation: {
readonly decode: SchemaGetter.Getter<
NoInfer<To["Encoded"]>,
NoInfer<From["Type"]>,
RD
>
readonly encode: SchemaGetter.Getter<
NoInfer<From["Type"]>,
NoInfer<To["Encoded"]>,
RE
>
}
transformation: {
readonly decode: SchemaGetter.Getter<
NoInfer<To["Encoded"]>,
NoInfer<From["Type"]>,
RD
>
(property) decode: {
run: (input: Option.Option<E>, options: SchemaAST.ParseOptions) => Effect.Effect<Option.Option<T>, SchemaIssue.Issue, R>;
map: (f: (t: NoInfer<To['Encoded']>) => T2) => SchemaGetter.Getter<T2, NoInfer<From['Type']>, RD>;
compose: (other: SchemaGetter.Getter<T2, NoInfer<To['Encoded']>, R2>) => SchemaGetter.Getter<T2, NoInfer<From['Type']>, RD | R2>;
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; <…;
}
decode: import SchemaGetterSchemaGetter.class Getter<out T, in E, R = never>class Getter {
run: (input: Option.Option<E>, options: SchemaAST.ParseOptions) => Effect.Effect<Option.Option<T>, SchemaIssue.Issue, R>;
map: <T2>(f: (t: T) => T2) => Getter<T2, E, R>;
compose: <T2, R2>(other: Getter<T2, T, R2>) => Getter<T2, E, R | R2>;
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; <…;
}
Represents a composable transformation from an encoded type E to a decoded type T.
When to use
Use when you need a schema getter to build and compose custom transformations
for Schema.decodeTo or Schema.decode.
Details
A getter wraps a function Option<E> -> Effect<Option<T>, Issue, R>. It
receives Option.None when the encoded key is absent, such as a missing
struct field, and returns Option.None to omit the value from the decoded
output. It fails with Issue on invalid input and may require Effect
services via R. .map(f) applies f to the decoded value inside Some
while leaving None unchanged. .compose(other) chains two getters by
feeding the output of this into other; passthrough getters on either side
are optimized away.
Example (Creating and composing getters)
import { SchemaGetter } from "effect"
const parseNumber = SchemaGetter.transform<number, string>((s) => Number(s))
const double = SchemaGetter.transform<number, number>((n) => n * 2)
const composed = parseNumber.compose(double)
// composed: Getter<number, string> — parses then doubles
Getter<type NoInfer<T> = intrinsicMarker for non-inference type position
NoInfer<function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
To["Encoded"]>, type NoInfer<T> = intrinsicMarker for non-inference type position
NoInfer<function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
From["Type"]>, function (type parameter) RD in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
RD>
readonly encode: SchemaGetter.Getter<
NoInfer<From["Type"]>,
NoInfer<To["Encoded"]>,
RE
>
(property) encode: {
run: (input: Option.Option<E>, options: SchemaAST.ParseOptions) => Effect.Effect<Option.Option<T>, SchemaIssue.Issue, R>;
map: (f: (t: NoInfer<From['Type']>) => T2) => SchemaGetter.Getter<T2, NoInfer<To['Encoded']>, RE>;
compose: (other: SchemaGetter.Getter<T2, NoInfer<From['Type']>, R2>) => SchemaGetter.Getter<T2, NoInfer<To['Encoded']>, RE | R2>;
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; <…;
}
encode: import SchemaGetterSchemaGetter.class Getter<out T, in E, R = never>class Getter {
run: (input: Option.Option<E>, options: SchemaAST.ParseOptions) => Effect.Effect<Option.Option<T>, SchemaIssue.Issue, R>;
map: <T2>(f: (t: T) => T2) => Getter<T2, E, R>;
compose: <T2, R2>(other: Getter<T2, T, R2>) => Getter<T2, E, R | R2>;
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; <…;
}
Represents a composable transformation from an encoded type E to a decoded type T.
When to use
Use when you need a schema getter to build and compose custom transformations
for Schema.decodeTo or Schema.decode.
Details
A getter wraps a function Option<E> -> Effect<Option<T>, Issue, R>. It
receives Option.None when the encoded key is absent, such as a missing
struct field, and returns Option.None to omit the value from the decoded
output. It fails with Issue on invalid input and may require Effect
services via R. .map(f) applies f to the decoded value inside Some
while leaving None unchanged. .compose(other) chains two getters by
feeding the output of this into other; passthrough getters on either side
are optimized away.
Example (Creating and composing getters)
import { SchemaGetter } from "effect"
const parseNumber = SchemaGetter.transform<number, string>((s) => Number(s))
const double = SchemaGetter.transform<number, number>((n) => n * 2)
const composed = parseNumber.compose(double)
// composed: Getter<number, string> — parses then doubles
Getter<type NoInfer<T> = intrinsicMarker for non-inference type position
NoInfer<function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
From["Type"]>, type NoInfer<T> = intrinsicMarker for non-inference type position
NoInfer<function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
To["Encoded"]>, function (type parameter) RE in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
RE>
}
): (from: From extends Constraintfrom: function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
From) => 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<function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
To, function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
From, function (type parameter) RD in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
RD, function (type parameter) RE in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation: {
readonly decode: SchemaGetter.Getter<NoInfer<To["Encoded"]>, NoInfer<From["Type"]>, RD>;
readonly encode: SchemaGetter.Getter<NoInfer<From["Type"]>, NoInfer<To["Encoded"]>, RE>;
}): (from: From) => decodeTo<To, From, RD, RE>
RE>
export function function decodeTo<To extends Constraint>(to: To): <From extends Constraint>(from: From) => compose<To, From> (+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<function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
To extends Constraint, function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
From extends Constraint, function (type parameter) RD in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
RD = never, function (type parameter) RE in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
RE = never>(
to: To extends Constraintto: function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
To,
transformation: | {
readonly decode: SchemaGetter.Getter<
To["Encoded"],
From["Type"],
RD
>
readonly encode: SchemaGetter.Getter<
From["Type"],
To["Encoded"],
RE
>
}
| undefined
transformation?: {
readonly decode: SchemaGetter.Getter<
To["Encoded"],
From["Type"],
RD
>
(property) decode: {
run: (input: Option.Option<E>, options: SchemaAST.ParseOptions) => Effect.Effect<Option.Option<T>, SchemaIssue.Issue, R>;
map: (f: (t: To['Encoded']) => T2) => SchemaGetter.Getter<T2, From['Type'], RD>;
compose: (other: SchemaGetter.Getter<T2, To['Encoded'], R2>) => SchemaGetter.Getter<T2, From['Type'], RD | R2>;
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; <…;
}
decode: import SchemaGetterSchemaGetter.class Getter<out T, in E, R = never>class Getter {
run: (input: Option.Option<E>, options: SchemaAST.ParseOptions) => Effect.Effect<Option.Option<T>, SchemaIssue.Issue, R>;
map: <T2>(f: (t: T) => T2) => Getter<T2, E, R>;
compose: <T2, R2>(other: Getter<T2, T, R2>) => Getter<T2, E, R | R2>;
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; <…;
}
Represents a composable transformation from an encoded type E to a decoded type T.
When to use
Use when you need a schema getter to build and compose custom transformations
for Schema.decodeTo or Schema.decode.
Details
A getter wraps a function Option<E> -> Effect<Option<T>, Issue, R>. It
receives Option.None when the encoded key is absent, such as a missing
struct field, and returns Option.None to omit the value from the decoded
output. It fails with Issue on invalid input and may require Effect
services via R. .map(f) applies f to the decoded value inside Some
while leaving None unchanged. .compose(other) chains two getters by
feeding the output of this into other; passthrough getters on either side
are optimized away.
Example (Creating and composing getters)
import { SchemaGetter } from "effect"
const parseNumber = SchemaGetter.transform<number, string>((s) => Number(s))
const double = SchemaGetter.transform<number, number>((n) => n * 2)
const composed = parseNumber.compose(double)
// composed: Getter<number, string> — parses then doubles
Getter<function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
To["Encoded"], function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
From["Type"], function (type parameter) RD in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
RD>
readonly encode: SchemaGetter.Getter<
From["Type"],
To["Encoded"],
RE
>
(property) encode: {
run: (input: Option.Option<E>, options: SchemaAST.ParseOptions) => Effect.Effect<Option.Option<T>, SchemaIssue.Issue, R>;
map: (f: (t: From['Type']) => T2) => SchemaGetter.Getter<T2, To['Encoded'], RE>;
compose: (other: SchemaGetter.Getter<T2, From['Type'], R2>) => SchemaGetter.Getter<T2, To['Encoded'], RE | R2>;
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; <…;
}
encode: import SchemaGetterSchemaGetter.class Getter<out T, in E, R = never>class Getter {
run: (input: Option.Option<E>, options: SchemaAST.ParseOptions) => Effect.Effect<Option.Option<T>, SchemaIssue.Issue, R>;
map: <T2>(f: (t: T) => T2) => Getter<T2, E, R>;
compose: <T2, R2>(other: Getter<T2, T, R2>) => Getter<T2, E, R | R2>;
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; <…;
}
Represents a composable transformation from an encoded type E to a decoded type T.
When to use
Use when you need a schema getter to build and compose custom transformations
for Schema.decodeTo or Schema.decode.
Details
A getter wraps a function Option<E> -> Effect<Option<T>, Issue, R>. It
receives Option.None when the encoded key is absent, such as a missing
struct field, and returns Option.None to omit the value from the decoded
output. It fails with Issue on invalid input and may require Effect
services via R. .map(f) applies f to the decoded value inside Some
while leaving None unchanged. .compose(other) chains two getters by
feeding the output of this into other; passthrough getters on either side
are optimized away.
Example (Creating and composing getters)
import { SchemaGetter } from "effect"
const parseNumber = SchemaGetter.transform<number, string>((s) => Number(s))
const double = SchemaGetter.transform<number, number>((n) => n * 2)
const composed = parseNumber.compose(double)
// composed: Getter<number, string> — parses then doubles
Getter<function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
From["Type"], function (type parameter) To in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
To["Encoded"], function (type parameter) RE in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
RE>
} | undefined
) {
return (from: From extends Constraintfrom: function (type parameter) From in decodeTo<To extends Constraint, From extends Constraint, RD = never, RE = never>(to: To, transformation?: {
readonly decode: SchemaGetter.Getter<To["Encoded"], From["Type"], RD>;
readonly encode: SchemaGetter.Getter<From["Type"], To["Encoded"], RE>;
} | undefined): (from: From) => Constraint
From) => {
return const make: <S extends Constraint>(
ast: S["ast"],
options?: object
) => S
Creates a schema from an AST (Abstract Syntax Tree) node.
Details
This is the fundamental constructor for all schemas in the Effect Schema
library. It takes an AST node and wraps it in a fully-typed schema that
preserves all type information and provides the complete schema API.
The make function is used internally to create all primitive schemas like
String, Number, Boolean, etc., as well as more complex schemas. It's
the bridge between the untyped AST representation and the strongly-typed
schema.
make(
import SchemaASTSchemaAST.function decodeTo<A extends AST>(
from: AST,
to: A,
transformation: SchemaTransformation.Transformation<
any,
any,
any,
any
>
): A
Attaches a Transformation to the to AST, making it decode from the
from AST and encode back to it.
Details
This is the low-level primitive behind Schema.transform and
Schema.transformOrFail. It appends a
Link
to the to node's
encoding chain.
- Returns a new AST with the same type as
to.
decodeTo(
from: From extends Constraintfrom.Constraint["ast"]: SchemaAST.ASTast,
to: To extends Constraintto.Constraint["ast"]: SchemaAST.ASTast,
transformation: | {
readonly decode: SchemaGetter.Getter<
To["Encoded"],
From["Type"],
RD
>
readonly encode: SchemaGetter.Getter<
From["Type"],
To["Encoded"],
RE
>
}
| undefined
transformation ? import SchemaTransformationSchemaTransformation.const make: <
T,
E,
RD = never,
RE = never
>(options: {
readonly decode: SchemaGetter.Getter<T, E, RD>
readonly encode: SchemaGetter.Getter<E, T, RE>
}) => Transformation<T, E, RD, RE>
Constructs a Transformation from an object with decode and encode
Getters. If the input is already a Transformation, returns it as-is.
When to use
Use when you already have schema getter instances and want to pair them into
a schema transformation.
- You want idempotent wrapping (won't double-wrap).
Details
- Returns the input unchanged if it is already a
Transformation.
Example (Wrapping existing getters)
import { SchemaGetter, SchemaTransformation } from "effect"
const t = SchemaTransformation.make({
decode: SchemaGetter.transform<number, string>((s) => Number(s)),
encode: SchemaGetter.transform<string, number>((n) => String(n))
})
make(transformation: | {
readonly decode: SchemaGetter.Getter<
To["Encoded"],
From["Type"],
RD
>
readonly encode: SchemaGetter.Getter<
From["Type"],
To["Encoded"],
RE
>
}
| undefined
transformation) : import SchemaTransformationSchemaTransformation.function passthrough<any>(): SchemaTransformation.Transformation<any, any, never, never> (+1 overload)Transforms values by returning the input unchanged in both
directions.
When to use
Use when you need a schema transformation to connect two schemas that share
the same type with no actual conversion.
Details
- Both decode and encode are no-ops.
- Returns a shared singleton instance (no allocation per call).
- By default,
T and E must be the same type. Pass { strict: false }
to bypass the type constraint.
Example (Chaining schemas with no conversion)
import { Schema, SchemaTransformation } from "effect"
const schema = Schema.Trim.pipe(
Schema.decodeTo(Schema.FiniteFromString, SchemaTransformation.passthrough())
)
passthrough()
),
{
from: From extends Constraintfrom,
to: To extends Constraintto
}
)
}
}