Skip to content

Schema Projections

Occasionally, you may need to derive a new schema from an existing one, specifically targeting either its Type or Encoded portion. The @effect/schema library provides several APIs to support this functionality.

The Schema.typeSchema function allows you to extract the Type portion of a schema, creating a new schema that conforms to the properties defined in the original schema without considering the initial encoding or transformation processes.

Function Signature

declare const typeSchema: <A, I, R>(schema: Schema<A, I, R>) => Schema<A>

Example

1
import {
import Schema
Schema
} from "@effect/schema"
2
3
const
const original: Schema.Struct<{ foo: Schema.filter<Schema.Schema<number, string, never>>; }>
original
=
import Schema
Schema
.
function Struct<{ foo: Schema.filter<Schema.Schema<number, string, never>>; }>(fields: { foo: Schema.filter<Schema.Schema<number, string, never>>; }): Schema.Struct<{ foo: Schema.filter<Schema.Schema<number, string, never>>; }> (+1 overload) namespace Struct
Struct
({
4
(property) foo: Schema.filter<Schema.Schema<number, string, never>>
foo
:
import Schema
Schema
.
class NumberFromString

This schema transforms a `string` into a `number` by parsing the string using the `parse` function of the `effect/Number` module. It returns an error if the value can't be converted (for example when non-numeric characters are provided). The following special string values are supported: "NaN", "Infinity", "-Infinity".

NumberFromString
.
(method) Pipeable.pipe<typeof Schema.NumberFromString, Schema.filter<Schema.Schema<number, string, never>>>(this: typeof Schema.NumberFromString, ab: (_: typeof Schema.NumberFromString) => Schema.filter<Schema.Schema<number, string, never>>): Schema.filter<...> (+21 overloads)
pipe
(
import Schema
Schema
.
const greaterThanOrEqualTo: <number>(min: number, annotations?: Schema.Annotations.Filter<number, number> | undefined) => <I, R>(self: Schema.Schema<number, I, R>) => Schema.filter<Schema.Schema<number, I, R>>

This filter checks whether the provided number is greater than or equal to the specified minimum.

greaterThanOrEqualTo
(2))
5
})
6
7
// This creates a schema where 'foo' is strictly a number
8
// that must be greater than or equal to 2.
9
const
const resultingTypeSchema: Schema.SchemaClass<{ readonly foo: number; }, { readonly foo: number; }, never>
resultingTypeSchema
=
import Schema
Schema
.
const typeSchema: <{ readonly foo: number; }, { readonly foo: string; }, never>(schema: Schema.Schema<{ readonly foo: number; }, { readonly foo: string; }, never>) => Schema.SchemaClass<{ readonly foo: number; }, { readonly foo: number; }, never>

The `typeSchema` function allows you to extract the `Type` portion of a schema, creating a new schema that conforms to the properties defined in the original schema without considering the initial encoding or transformation processes.

typeSchema
(
const original: Schema.Struct<{ foo: Schema.filter<Schema.Schema<number, string, never>>; }>
original
)
10
11
// resultingTypeSchema is the same as:
12
const
const resultingTypeSchema2: Schema.Struct<{ foo: Schema.filter<Schema.Schema<number, number, never>>; }>
resultingTypeSchema2
=
import Schema
Schema
.
function Struct<{ foo: Schema.filter<Schema.Schema<number, number, never>>; }>(fields: { foo: Schema.filter<Schema.Schema<number, number, never>>; }): Schema.Struct<{ foo: Schema.filter<Schema.Schema<number, number, never>>; }> (+1 overload) namespace Struct
Struct
({
13
(property) foo: Schema.filter<Schema.Schema<number, number, never>>
foo
:
import Schema
Schema
.
(alias) class Number export Number
Number
.
(method) Pipeable.pipe<typeof Schema.Number, Schema.filter<Schema.Schema<number, number, never>>>(this: typeof Schema.Number, ab: (_: typeof Schema.Number) => Schema.filter<Schema.Schema<number, number, never>>): Schema.filter<...> (+21 overloads)
pipe
(
import Schema
Schema
.
const greaterThanOrEqualTo: <number>(min: number, annotations?: Schema.Annotations.Filter<number, number> | undefined) => <I, R>(self: Schema.Schema<number, I, R>) => Schema.filter<Schema.Schema<number, I, R>>

This filter checks whether the provided number is greater than or equal to the specified minimum.

greaterThanOrEqualTo
(2))
14
})

In this example:

  • Original Schema: The schema for foo is initially defined to accept a number from a string and enforce that it is greater than or equal to 2.
  • Resulting Type Schema: The Schema.typeSchema function extracts only the type-related information from foo, simplifying it to just a number while still maintaining the constraint that it must be greater than or equal to 2.

The Schema.encodedSchema function allows you to extract the Encoded portion of a schema, creating a new schema that conforms to the properties defined in the original schema without retaining any refinements or transformations that were applied previously.

Function Signature

declare const encodedSchema: <A, I, R>(
schema: Schema<A, I, R>
) => Schema<I>

Example

1
import {
import Schema
Schema
} from "@effect/schema"
2
3
const
const original: Schema.Struct<{ foo: Schema.filter<Schema.Schema<string, string, never>>; }>
original
=
import Schema
Schema
.
function Struct<{ foo: Schema.filter<Schema.Schema<string, string, never>>; }>(fields: { foo: Schema.filter<Schema.Schema<string, string, never>>; }): Schema.Struct<{ foo: Schema.filter<Schema.Schema<string, string, never>>; }> (+1 overload) namespace Struct
Struct
({
4
(property) foo: Schema.filter<Schema.Schema<string, string, never>>
foo
:
import Schema
Schema
.
(alias) class String export String
String
.
(method) Pipeable.pipe<typeof Schema.String, Schema.filter<Schema.Schema<string, string, never>>>(this: typeof Schema.String, ab: (_: typeof Schema.String) => Schema.filter<Schema.Schema<string, string, never>>): Schema.filter<...> (+21 overloads)
pipe
(
import Schema
Schema
.
const minLength: <string>(minLength: number, annotations?: Schema.Annotations.Filter<string, string> | undefined) => <I, R>(self: Schema.Schema<string, I, R>) => Schema.filter<...>
minLength
(3))
5
})
6
7
// resultingEncodedSchema simplifies 'foo' to just a string,
8
// disregarding the minLength refinement.
9
const
const resultingEncodedSchema: Schema.SchemaClass<{ readonly foo: string; }, { readonly foo: string; }, never>
resultingEncodedSchema
=
import Schema
Schema
.
const encodedSchema: <{ readonly foo: string; }, { readonly foo: string; }, never>(schema: Schema.Schema<{ readonly foo: string; }, { readonly foo: string; }, never>) => Schema.SchemaClass<{ readonly foo: string; }, { readonly foo: string; }, never>

The `encodedSchema` function allows you to extract the `Encoded` portion of a schema, creating a new schema that conforms to the properties defined in the original schema without retaining any refinements or transformations that were applied previously.

encodedSchema
(
const original: Schema.Struct<{ foo: Schema.filter<Schema.Schema<string, string, never>>; }>
original
)
10
11
// resultingEncodedSchema is the same as:
12
const
const resultingEncodedSchema2: Schema.Struct<{ foo: typeof Schema.String; }>
resultingEncodedSchema2
=
import Schema
Schema
.
function Struct<{ foo: typeof Schema.String; }>(fields: { foo: typeof Schema.String; }): Schema.Struct<{ foo: typeof Schema.String; }> (+1 overload) namespace Struct
Struct
({
13
(property) foo: typeof Schema.String
foo
:
import Schema
Schema
.
(alias) class String export String
String
14
})

In this example:

  • Original Schema Definition: The foo field in the schema is defined as a string with a minimum length of three characters.
  • Resulting Encoded Schema: The encodedSchema function simplifies the foo field to just a string type, effectively stripping away the minLength refinement.

The Schema.encodedBoundSchema function is similar to Schema.encodedSchema but preserves the refinements up to the first transformation point in the original schema.

Function Signature

declare const encodedBoundSchema: <A, I, R>(
schema: Schema<A, I, R>
) => Schema<I>

The term “bound” in this context refers to the boundary up to which refinements are preserved when extracting the encoded form of a schema. It essentially marks the limit to which initial validations and structure are maintained before any transformations are applied.

Example

1
import {
import Schema
Schema
} from "@effect/schema"
2
3
const
const original: Schema.Struct<{ foo: Schema.SchemaClass<string, string, never>; }>
original
=
import Schema
Schema
.
function Struct<{ foo: Schema.SchemaClass<string, string, never>; }>(fields: { foo: Schema.SchemaClass<string, string, never>; }): Schema.Struct<{ foo: Schema.SchemaClass<string, string, never>; }> (+1 overload) namespace Struct
Struct
({
4
(property) foo: Schema.SchemaClass<string, string, never>
foo
:
import Schema
Schema
.
(alias) class String export String
String
.
(method) Pipeable.pipe<typeof Schema.String, Schema.filter<Schema.Schema<string, string, never>>, Schema.SchemaClass<string, string, never>>(this: typeof Schema.String, ab: (_: typeof Schema.String) => Schema.filter<...>, bc: (_: Schema.filter<...>) => Schema.SchemaClass<...>): Schema.SchemaClass<...> (+21 overloads)
pipe
(
5
import Schema
Schema
.
const minLength: <string>(minLength: number, annotations?: Schema.Annotations.Filter<string, string> | undefined) => <I, R>(self: Schema.Schema<string, I, R>) => Schema.filter<...>
minLength
(3),
6
import Schema
Schema
.
const compose: <string, string, never, string>(to: Schema.Schema<string, string, never>) => <A, R1>(from: Schema.Schema<string, A, R1>) => Schema.SchemaClass<string, A, R1> (+7 overloads)
compose
(
import Schema
Schema
.
class Trim

This schema allows removing whitespaces from the beginning and end of a string.

Trim
)
7
)
8
})
9
10
// The resultingEncodedBoundSchema preserves the minLength(3) refinement,
11
// ensuring the string length condition is enforced but omits the Trim transformation.
12
const
const resultingEncodedBoundSchema: Schema.SchemaClass<{ readonly foo: string; }, { readonly foo: string; }, never>
resultingEncodedBoundSchema
=
import Schema
Schema
.
const encodedBoundSchema: <{ readonly foo: string; }, { readonly foo: string; }, never>(schema: Schema.Schema<{ readonly foo: string; }, { readonly foo: string; }, never>) => Schema.SchemaClass<{ readonly foo: string; }, { readonly foo: string; }, never>

The `encodedBoundSchema` function is similar to `encodedSchema` but preserves the refinements up to the first transformation point in the original schema.

encodedBoundSchema
(
const original: Schema.Struct<{ foo: Schema.SchemaClass<string, string, never>; }>
original
)
13
14
// resultingEncodedBoundSchema is the same as:
15
const
const resultingEncodedBoundSchema2: Schema.Struct<{ foo: Schema.filter<Schema.Schema<string, string, never>>; }>
resultingEncodedBoundSchema2
=
import Schema
Schema
.
function Struct<{ foo: Schema.filter<Schema.Schema<string, string, never>>; }>(fields: { foo: Schema.filter<Schema.Schema<string, string, never>>; }): Schema.Struct<{ foo: Schema.filter<Schema.Schema<string, string, never>>; }> (+1 overload) namespace Struct
Struct
({
16
(property) foo: Schema.filter<Schema.Schema<string, string, never>>
foo
:
import Schema
Schema
.
(alias) class String export String
String
.
(method) Pipeable.pipe<typeof Schema.String, Schema.filter<Schema.Schema<string, string, never>>>(this: typeof Schema.String, ab: (_: typeof Schema.String) => Schema.filter<Schema.Schema<string, string, never>>): Schema.filter<...> (+21 overloads)
pipe
(
import Schema
Schema
.
const minLength: <string>(minLength: number, annotations?: Schema.Annotations.Filter<string, string> | undefined) => <I, R>(self: Schema.Schema<string, I, R>) => Schema.filter<...>
minLength
(3))
17
})

In the provided example:

  • Initial Schema: The schema for foo includes a refinement to ensure strings have a minimum length of three characters and a transformation to trim the string.
  • Resulting Schema: resultingEncodedBoundSchema maintains the Schema.minLength(3) condition, ensuring that this validation persists. However, it excludes the trimming transformation, focusing solely on the length requirement without altering the string’s formatting.