Schema: Migration from v3
This document maps v3 Schema APIs to their v4 equivalents. Simple renames and argument changes are covered in the summary table below. More complex migrations have dedicated sections with code examples.
Migration types
Section titled “Migration types”- auto — mechanical find-and-replace, safe to auto-apply
- semi-auto — follows a clear pattern but needs structural changes
- manual — requires case-by-case decisions, flag for human review
- removed — no v4 equivalent
Summary table
Section titled “Summary table”| v3 API | v4 API | Migration type |
|---|---|---|
asSchema(schema) |
revealCodec(schema) |
rename |
encodedSchema(schema) |
toEncoded(schema) |
rename |
typeSchema(schema) |
toType(schema) |
rename |
compose(schemaB) |
decodeTo(schemaB) |
rename |
annotations(ann) |
annotate(ann) |
rename |
decodingFallback annotation |
catchDecoding(...) |
rename |
parseJson() |
UnknownFromJsonString |
rename |
parseJson(schema) |
fromJsonString(schema) |
rename |
pattern(regex) |
check(isPattern(regex)) |
rename |
nonEmptyString |
isNonEmpty |
rename |
BigIntFromSelf |
BigInt |
rename |
SymbolFromSelf |
Symbol |
rename |
URLFromSelf |
URL |
rename |
RedactedFromSelf |
Redacted |
rename |
Redacted |
RedactedFromValue |
rename |
EitherFromSelf |
Result |
rename |
TaggedError |
TaggedErrorClass |
rename |
decodeUnknown |
decodeUnknownEffect |
rename |
decode |
decodeEffect |
rename |
decodeUnknownEither |
decodeUnknownExit |
rename |
decodeEither |
decodeExit |
rename |
encodeUnknown |
encodeUnknownEffect |
rename |
encode |
encodeEffect |
rename |
encodeUnknownEither |
encodeUnknownExit |
rename |
encodeEither |
encodeExit |
rename |
asserts(schema)(input) |
asserts(schema, input) |
semi-auto |
Literal(null) |
Null |
restructure |
Literal("a", "b") |
Literals(["a", "b"]) |
variadic-to-array |
pickLiteral("a", "b") |
Literals(...).pick(["a", "b"]) |
restructure |
Union(A, B) |
Union([A, B]) |
variadic-to-array |
Tuple(A, B) |
Tuple([A, B]) |
variadic-to-array |
TemplateLiteral(A, B) |
TemplateLiteral([A, B]) |
variadic-to-array |
TemplateLiteralParser(A, B) |
TemplateLiteralParser(schema.parts) |
restructure |
Record({ key, value }) |
Record(key, value) |
restructure |
filter(predicate) |
check(makeFilter(predicate)) |
restructure |
filter(refinement) |
refine(refinement) |
restructure |
UUID |
String.check(isUUID()) |
restructure |
ULID |
String.check(isULID()) |
restructure |
pick("a") |
mapFields(Struct.pick(["a"])) |
restructure |
omit("a") |
mapFields(Struct.omit(["a"])) |
restructure |
partial |
mapFields(Struct.map(Schema.optional)) |
restructure |
partialWith({ exact: true }) |
mapFields(Struct.map(Schema.optionalKey)) |
restructure |
required(schema) |
schema.mapFields(Struct.map(Schema.requiredKey)) |
restructure |
extend(structB) |
mapFields(Struct.assign(fieldsB)) or fieldsAssign(fieldsB) |
restructure |
transform(from, to, { decode, encode }) |
from.pipe(decodeTo(to, SchemaTransformation.transform({ decode, encode }))) |
restructure |
transformOrFail(from, to, { decode, encode }) |
from.pipe(decodeTo(to, { decode: SchemaGetter.transformOrFail(...), ... })) |
restructure |
transformLiteral(from, to) |
Literal(from).transform(to) |
restructure |
transformLiterals([0,"a"], [1,"b"]) |
Literals([0, 1]).transform(["a", "b"]) |
restructure |
attachPropertySignature("k", "v") |
mapFields(f => ({...f, k: tagDefaultOmit("v")})) |
restructure |
validate* |
removed (use decode* + toType) |
removed |
keyof |
— | removed |
NonEmptyArrayEnsure |
— | removed |
withDefaults |
— | removed |
Data(schema) |
— | removed |
optionalWith(schema, opts) |
varies by options (see optionalWith) | manual |
optionalToOptional |
see optional field transformations | manual |
optionalToRequired |
see optional field transformations | manual |
requiredToOptional |
see optional field transformations | manual |
filterEffect |
see filterEffect | manual |
fromKey |
see rename | manual |
rename({ a: "c" }) |
see rename | manual |
format(schema) |
see format | manual |
ParseResult.ArrayFormatter.formatError(error) |
see ParseResult formatters | manual |
declare |
see declare | manual |
Additional rename notes
Section titled “Additional rename notes”*FromSelf renames
Section titled “*FromSelf renames”The following *FromSelf schemas have been renamed to drop the suffix:
DateFromSelf → Date, DurationFromSelf → Duration, ChunkFromSelf → Chunk, ReadonlyMapFromSelf → ReadonlyMap, ReadonlySetFromSelf → ReadonlySet, HashMapFromSelf → HashMap, HashSetFromSelf → HashSet, BigDecimalFromSelf → BigDecimal, CauseFromSelf → Cause, ExitFromSelf → Exit, OptionFromSelf → Option, RegExpFromSelf → RegExp
Filter renames
Section titled “Filter renames”All filters have been renamed with an is prefix and now use check(...) or pipe(Schema.check(...)):
greaterThan → isGreaterThan, greaterThanOrEqualTo → isGreaterThanOrEqualTo, lessThan → isLessThan, lessThanOrEqualTo → isLessThanOrEqualTo, between → isBetween, int → isInt, multipleOf → isMultipleOf, finite → isFinite, minLength → isMinLength, maxLength → isMaxLength, length → isLengthBetween
Note: positive, negative, nonNegative, nonPositive have been removed in v4.
Utility renames
Section titled “Utility renames”equivalence → toEquivalence, arbitrary → toArbitrary, pretty → toFormatter, standardSchemaV1 → toStandardSchemaV1
Detailed migrations
Section titled “Detailed migrations”Redacted
Section titled “Redacted”Migration: rename with behavior distinction
In v3, Schema.Redacted(value) decoded the raw encoded value and wrapped the decoded value in Redacted.
In v4, that behavior is named Schema.RedactedFromValue(value).
v3
import { Schema } from "effect"
const schema = Schema.Redacted(Schema.String)const decode = Schema.decodeSync(schema)
decode("secret")v4
import { Redacted, Schema } from "effect"
const schema = Schema.RedactedFromValue(Schema.String)const decode = Schema.decodeSync(schema)
const redacted = decode("secret")console.log(Redacted.value(redacted))// secretSchema.Redacted(value) in v4 is the replacement for v3 Schema.RedactedFromSelf(value): it expects the input to already be a Redacted value, so both Type and Encoded are Redacted<...>.
asserts signature
Section titled “asserts signature”Migration: semi-auto
Schema.asserts now asserts an input directly instead of returning an assertion function.
v3
import { Schema } from "effect"
const assertString = Schema.asserts(Schema.String)assertString(input)v4
import { Schema } from "effect"
Schema.asserts(Schema.String, input)validate* removal
Section titled “validate* removal”Migration: removed
The validate, validateEither, validatePromise, validateSync, and validateOption APIs have been removed. Use Schema.decode* + Schema.toType instead.
import { Schema } from "effect"
// v3: Schema.validateSync(Schema.String)(input)// v4:const validateSync = Schema.decodeSync(Schema.toType(Schema.String))Data removal
Section titled “Data removal”Migration: removed
Schema.Data has no v4 equivalent. Remove it. Equal.equals performs deep structural comparison on objects by default in v4, so Schema.Data is unnecessary.
pickLiterals
Section titled “pickLiterals”Migration: auto
v3
import { Schema } from "effect"
const schema = Schema.Literal("a", "b", "c").pipe(Schema.pickLiteral("a", "b"))v4
import { Schema } from "effect"
const schema = Schema.Literals(["a", "b", "c"]).pick(["a", "b"])TemplateLiteralParser
Section titled “TemplateLiteralParser”Migration: semi-auto
v3
import { Schema } from "effect"
const schema = Schema.TemplateLiteral(Schema.String, ".", Schema.String)const parser = Schema.TemplateLiteralParser(Schema.String, ".", Schema.String)v4
import { Schema } from "effect"
const schema = Schema.TemplateLiteral([Schema.String, ".", Schema.String])// use the `parts` property instead of repeating the template partsconst parser = Schema.TemplateLiteralParser(schema.parts)Behavior note: TemplateLiteral and TemplateLiteralParser match parts semantically. Checks on string, number, and bigint schema parts are applied while matching each segment, so refined parts can reject strings that would match the broader primitive shape.
format
Section titled “format”Migration: manual
New imports: SchemaRepresentation
v3
import { Schema } from "effect"
console.log(Schema.format(Schema.String))// stringv4
import { Schema, SchemaRepresentation } from "effect"
const doc = SchemaRepresentation.fromAST(Schema.String.ast)const multi = SchemaRepresentation.toMultiDocument(doc)const codeDoc = SchemaRepresentation.toCodeDocument(multi)console.log(codeDoc.codes[0].Type)// stringParseResult formatters
Section titled “ParseResult formatters”Migration: manual
New imports: SchemaIssue
In v4, schema parsing fails with Schema.SchemaError, which contains a nested SchemaIssue in its issue field.
Use SchemaIssue.makeFormatterStandardSchemaV1()(error.issue).issues for the v3 ParseResult.ArrayFormatter.formatError(error) equivalent.
v3
import { Either, ParseResult, Schema } from "effect"
const Person = Schema.Struct({ name: Schema.String, age: Schema.Number})
const decode = Schema.decodeUnknownEither(Person)
const result = decode({})if (Either.isLeft(result)) { console.error("Decoding failed:") console.error(ParseResult.ArrayFormatter.formatErrorSync(result.left))}/*Decoding failed:[ { _tag: 'Missing', path: [ 'name' ], message: 'is missing' } ]*/v4
import { Schema, SchemaIssue } from "effect"
const Person = Schema.Struct({ name: Schema.String, age: Schema.Number})
const decode = Schema.decodeUnknownSync(Person)
try { decode({})} catch (error) { if (error instanceof Error) { console.error("Decoding failed:") if (SchemaIssue.isIssue(error.cause)) { console.error(SchemaIssue.makeFormatterStandardSchemaV1()(error.cause).issues) } }}/*Decoding failed:[ { path: [ 'name' ], message: 'Missing key' } ]*/Record
Section titled “Record”Migration: auto
v3
import { Schema } from "effect"
const schema = Schema.Record({ key: Schema.String, value: Schema.Number })v4
import { Schema } from "effect"
const schema = Schema.Record(Schema.String, Schema.Number)Behavior note: dynamic record key schemas select matching own properties before the value schema is applied. Refined key schemas such as Schema.String.check(...), Schema.Int, or checked template literals ignore properties that do not match the key schema; they do not validate the value at those ignored keys. For transformed key schemas, selection is based on encoded property names before selected keys are decoded.
pick / omit
Section titled “pick / omit”Migration: semi-auto
New imports: Struct
v3
import { Schema } from "effect"
const picked = Schema.Struct({ a: Schema.String, b: Schema.Number }).pipe(Schema.pick("a"))const omitted = Schema.Struct({ a: Schema.String, b: Schema.Number }).pipe(Schema.omit("b"))v4
import { Schema, Struct } from "effect"
const picked = Schema.Struct({ a: Schema.String, b: Schema.Number }).mapFields(Struct.pick(["a"]))const omitted = Schema.Struct({ a: Schema.String, b: Schema.Number }).mapFields(Struct.omit(["b"]))partial / partialWith
Section titled “partial / partialWith”Migration: semi-auto
New imports: Struct
Schema.partial→mapFields(Struct.map(Schema.optional))(allowsundefined)Schema.partialWith({ exact: true })→mapFields(Struct.map(Schema.optionalKey))(exact)
import { Schema, Struct } from "effect"
const struct = Schema.Struct({ a: Schema.String, b: Schema.Number })
// v3: struct.pipe(Schema.partial)const withUndefined = struct.mapFields(Struct.map(Schema.optional))
// v3: struct.pipe(Schema.partialWith({ exact: true }))const exact = struct.mapFields(Struct.map(Schema.optionalKey))You can also make a subset of fields partial:
import { Schema, Struct } from "effect"
const schema = Schema.Struct({ a: Schema.String, b: Schema.Number }) .mapFields(Struct.mapPick(["a"], Schema.optional))required
Section titled “required”Migration: semi-auto
New imports: Struct
Schema.requiredKey: makesoptionalKeyfields required; makesoptionalfields required asT | undefinedSchema.required: makesoptionalfields required (removesundefined)
import { Schema, Struct } from "effect"
const original = Schema.Struct({ a: Schema.optionalKey(Schema.String), b: Schema.optionalKey(Schema.Number)})
// v3: Schema.required(original)const schema = original.mapFields(Struct.map(Schema.requiredKey))// { readonly a: string; readonly b: number; }optional field transformations
Section titled “optional field transformations”Migration: manual
New imports: SchemaGetter
optionalToOptional, optionalToRequired, and requiredToOptional are all replaced by Schema.decodeTo + SchemaGetter.transformOptional.
The pattern: start with the encoded optionality (optionalKey or required), pipe to decodeTo with the decoded optionality, and provide transformOptional functions for decode/encode.
Example (v3 optionalToRequired: setting null as default for missing field)
v3
import { Option, Schema } from "effect"
const schema = Schema.Struct({ a: Schema.optionalToRequired(Schema.String, Schema.NullOr(Schema.String), { decode: Option.getOrElse(() => null), encode: Option.liftPredicate((value) => value !== null) })})v4
import { Option, Schema, SchemaGetter } from "effect"
const schema = Schema.Struct({ a: Schema.optionalKey(Schema.String).pipe( Schema.decodeTo(Schema.NullOr(Schema.String), { decode: SchemaGetter.transformOptional(Option.orElseSome(() => null)), encode: SchemaGetter.transformOptional(Option.filter((value) => value !== null)) }) )})Example (v3 requiredToOptional: empty string as missing value)
v3
import { Option, Schema } from "effect"
const schema = Schema.Struct({ a: Schema.requiredToOptional(Schema.String, Schema.String, { decode: Option.liftPredicate((s) => s !== ""), encode: Option.getOrElse(() => "") })})v4
import { Option, Schema, SchemaGetter } from "effect"
const schema = Schema.Struct({ a: Schema.String.pipe( Schema.decodeTo(Schema.optionalKey(Schema.String), { decode: SchemaGetter.transformOptional(Option.filter((value) => value !== "")), encode: SchemaGetter.transformOptional(Option.orElseSome(() => "")) }) )})optionalWith
Section titled “optionalWith”Migration: manual
New imports: SchemaGetter, Predicate (for nullable variants)
Decision tree
Section titled “Decision tree”| v3 options | v4 pattern |
|---|---|
{ exact: true } |
optionalKey(schema) |
{ default } |
schema.pipe(withDecodingDefaultType(...)) |
{ exact: true, default } |
schema.pipe(withDecodingDefaultTypeKey(...)) |
{ nullable: true } |
optional(NullOr(schema)) + decodeTo + filter null |
{ nullable: true, exact: true } |
optionalKey(NullOr(schema)) + decodeTo + filter null |
{ nullable: true, default } |
optional(NullOr(schema)) + decodeTo + filter null + orElseSome |
{ nullable: true, exact: true, default } |
optionalKey(NullOr(schema)) + decodeTo + filter null + orElseSome |
Key rules:
exact: true→ useoptionalKeyinstead ofoptionalnullable: true→ wrap inner schema inNullOrand filter nulls viaOption.filter(Predicate.isNotNull)default→ usewithDecodingDefaultType(orwithDecodingDefaultTypeKeywithexact: true)
Example: { exact: true } (simplest case)
Section titled “Example: { exact: true } (simplest case)”v3
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.optionalWith(Schema.NumberFromString, { exact: true })})v4
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.optionalKey(Schema.NumberFromString)})Example: { default }
Section titled “Example: { default }”v3
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.optionalWith(Schema.String, { default: () => "" })})v4
import { Effect, Schema } from "effect"
const schema = Schema.Struct({ a: Schema.String.pipe(Schema.withDecodingDefaultType(Effect.succeed("")))})Example: { exact: true, default }
Section titled “Example: { exact: true, default }”v3
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.optionalWith(Schema.String, { exact: true, default: () => "" })})v4
import { Effect, Schema } from "effect"
const schema = Schema.Struct({ a: Schema.String.pipe(Schema.withDecodingDefaultTypeKey(Effect.succeed("")))})Example: { nullable: true, exact: true, default } (most complex case)
Section titled “Example: { nullable: true, exact: true, default } (most complex case)”v3
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.optionalWith(Schema.NumberFromString, { nullable: true, default: () => -1, exact: true })})v4
import { Option, Predicate, Schema, SchemaGetter } from "effect"
const schema = Schema.Struct({ a: Schema.optionalKey(Schema.NullOr(Schema.NumberFromString)).pipe( Schema.decodeTo(Schema.Number, { decode: SchemaGetter.transformOptional((o) => o.pipe(Option.filter(Predicate.isNotNull), Option.orElseSome(() => -1)) ), encode: SchemaGetter.required() }) )})Migration: manual
New imports: SchemaGetter, Struct
v3
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.String, b: Schema.Number }).pipe(Schema.pluck("a"))v4
import { Schema, SchemaGetter, Struct } from "effect"
function pluck<P extends PropertyKey>(key: P) { return <S extends Schema.Top>( schema: Schema.Struct<{ [K in P]: S }> ): Schema.decodeTo<Schema.toType<S>, Schema.Struct<{ [K in P]: S }>> => { return schema.mapFields(Struct.pick([key])).pipe( Schema.decodeTo(Schema.toType(schema.fields[key]), { decode: SchemaGetter.transform((whole: any) => whole[key]), encode: SchemaGetter.transform((value) => ({ [key]: value } as any)) }) ) }}
const schema = Schema.Struct({ a: Schema.String, b: Schema.Number }).pipe(pluck("a"))extend
Section titled “extend”Migration: semi-auto
New imports: Struct (Struct case), Tuple (Union case)
Struct extends Struct
Section titled “Struct extends Struct”v3
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.String, b: Schema.Number}).pipe(Schema.extend(Schema.Struct({ c: Schema.Number })))v4
import { Schema, Struct } from "effect"
const schema = Schema.Struct({ a: Schema.String, b: Schema.Number}).mapFields(Struct.assign({ c: Schema.Number }))
// or more succinctlyconst schema2 = Schema.Struct({ a: Schema.String, b: Schema.Number}).pipe(Schema.fieldsAssign({ c: Schema.Number }))Union extends Struct
Section titled “Union extends Struct”v3
import { Schema } from "effect"
const schema = Schema.Union( Schema.Struct({ a: Schema.String }), Schema.Struct({ b: Schema.Number })).pipe(Schema.extend(Schema.Struct({ c: Schema.Boolean })))v4
import { Schema, Tuple } from "effect"
const schema = Schema.Union([ Schema.Struct({ a: Schema.String }), Schema.Struct({ b: Schema.Number })]).mapMembers(Tuple.map(Schema.fieldsAssign({ c: Schema.Number })))filter
Section titled “filter”Migration: semi-auto
v3
import { Schema } from "effect"
// inline filterconst a = Schema.String.pipe(Schema.filter((s) => s.length > 0))
// refinementconst b = Schema.Option(Schema.String).pipe(Schema.filter(Option.isSome))v4
import { Option, Schema } from "effect"
// inline filterconst a = Schema.String.check(Schema.makeFilter((s) => s.length > 0))
// refinementconst b = Schema.Option(Schema.String).pipe(Schema.refine(Option.isSome))In v4, a makeFilter predicate can return any of the shapes described by Schema.FilterOutput:
undefined/true— successfalse— generic failurestring— failure with that messageSchemaIssue.Issue— a fully-formed issue{ path, issue }— failure at a nested path (issueis astringorSchemaIssue.Issue)ReadonlyArray<Schema.FilterIssue>— several failures reported together (empty array = success, single element is unwrapped, otherwise grouped into anIssue.Composite)
Example (Failure at a nested path)
import { Schema } from "effect"
const schema = Schema.Struct({ password: Schema.String, confirmPassword: Schema.String }).check( Schema.makeFilter((o) => o.password === o.confirmPassword ? undefined : { path: ["password"], issue: "password and confirmPassword must match" } ))
console.log(String(Schema.decodeUnknownExit(schema)({ password: "123456", confirmPassword: "1234567" })))// Failure(Cause([Fail(SchemaError: password and confirmPassword must match// at ["password"])]))Example (Reporting multiple failures at once)
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.Finite, b: Schema.Finite, c: Schema.Finite }).check( Schema.makeFilter((o) => { const issues: Array<Schema.FilterIssue> = [] if (o.a > 0) { if (o.b <= 0) issues.push({ path: ["b"], issue: "b must be greater than 0" }) if (o.c <= 0) issues.push({ path: ["c"], issue: "c must be greater than 0" }) } return issues }))
console.log(String(Schema.decodeUnknownExit(schema)({ a: 1, b: 0, c: 0 })))// Failure(Cause([Fail(SchemaError: b must be greater than 0// at ["b"]// c must be greater than 0// at ["c"])]))filterEffect
Section titled “filterEffect”Migration: manual
New imports: SchemaGetter, Result
v3
import { Effect, Schema } from "effect"
async function validateUsername(username: string) { return Promise.resolve(username === "gcanti")}
const ValidUsername = Schema.String.pipe( Schema.filterEffect((username) => Effect.promise(() => validateUsername(username).then((valid) => valid || "Invalid username")) ))v4
import { Effect, Result, Schema, SchemaGetter } from "effect"
async function validateUsername(username: string) { return Promise.resolve(username === "gcanti")}
const ValidUsername = Schema.String.pipe( Schema.decode({ decode: SchemaGetter.checkEffect((username) => Effect.promise(() => validateUsername(username).then((valid) => valid || "Invalid username")) ), encode: SchemaGetter.passthrough() }))transform
Section titled “transform”Migration: semi-auto
New imports: SchemaTransformation
v3
import { Schema } from "effect"
const BooleanFromString = Schema.transform(Schema.Literal("on", "off"), Schema.Boolean, { strict: true, decode: (literal) => literal === "on", encode: (bool) => (bool ? "on" : "off")})v4
import { Schema, SchemaTransformation } from "effect"
const BooleanFromString = Schema.Literals(["on", "off"]).pipe( Schema.decodeTo( Schema.Boolean, SchemaTransformation.transform({ decode: (literal) => literal === "on", encode: (bool) => (bool ? "on" : "off") }) ))transformOrFail
Section titled “transformOrFail”Migration: semi-auto
New imports: SchemaGetter, SchemaIssue
v3
import { ParseResult, Schema } from "effect"
const NumberFromString = Schema.transformOrFail(Schema.String, Schema.Number, { strict: true, decode: (input, _, ast) => { const parsed = parseFloat(input) if (isNaN(parsed)) { return ParseResult.fail(new ParseResult.Type(ast, input, "Failed to convert string to number")) } return ParseResult.succeed(parsed) }, encode: (input) => ParseResult.succeed(input.toString())})v4
import { Effect, Number, Option, Schema, SchemaGetter, SchemaIssue } from "effect"
const NumberFromString = Schema.String.pipe( Schema.decodeTo(Schema.Number, { decode: SchemaGetter.transformOrFail((s) => { const n = Number.parse(s) if (n === undefined) { return Effect.fail(new SchemaIssue.InvalidValue(Option.some(s))) } return Effect.succeed(n) }), encode: SchemaGetter.String() }))transformLiteral / transformLiterals
Section titled “transformLiteral / transformLiterals”Migration: auto
v3
import { Schema } from "effect"
const a = Schema.transformLiteral(0, "a")const b = Schema.transformLiterals([0, "a"], [1, "b"], [2, "c"])v4
import { Schema } from "effect"
const a = Schema.Literal(0).transform("a")const b = Schema.Literals([0, 1, 2]).transform(["a", "b", "c"])attachPropertySignature
Section titled “attachPropertySignature”Migration: semi-auto
v3
import { Schema } from "effect"
const Circle = Schema.Struct({ radius: Schema.Number })const Square = Schema.Struct({ sideLength: Schema.Number })
const DiscriminatedShape = Schema.Union( Circle.pipe(Schema.attachPropertySignature("kind", "circle")), Square.pipe(Schema.attachPropertySignature("kind", "square")))v4
import { Schema } from "effect"
const Circle = Schema.Struct({ radius: Schema.Number })const Square = Schema.Struct({ sideLength: Schema.Number })
const DiscriminatedShape = Schema.Union([ Circle.mapFields((fields) => ({ ...fields, kind: Schema.tagDefaultOmit("circle") })), Square.mapFields((fields) => ({ ...fields, kind: Schema.tagDefaultOmit("square") }))])decodingFallback
Section titled “decodingFallback”Migration: auto
v3
import { Effect, Schema } from "effect"
const schema = Schema.String.annotations({ decodingFallback: () => Effect.succeed("a")})v4
import { Effect, Schema } from "effect"
const schema = Schema.String.pipe(Schema.catchDecoding(() => Effect.succeedSome("a")))rename
Section titled “rename”Migration: manual
New imports: SchemaTransformation
v3
import { Schema } from "effect"
const schema = Schema.Struct({ a: Schema.String, b: Schema.Number}).pipe(Schema.rename({ a: "c" }))v4
import { Schema } from "effect"
// experimental APIconst schema = Schema.Struct({ a: Schema.String, b: Schema.Number}).pipe(Schema.encodeKeys({ a: "c" }))Capitalize / Lowercase / Uppercase / Uncapitalize
Section titled “Capitalize / Lowercase / Uppercase / Uncapitalize”Migration: semi-auto
New imports: SchemaTransformation
v3
import { Schema } from "effect"
const schema = Schema.Capitalizev4
import { Schema, SchemaTransformation } from "effect"
const schema = Schema.String.pipe( Schema.decodeTo(Schema.String.check(Schema.isCapitalized()), SchemaTransformation.capitalize()))NonEmptyTrimmedString
Section titled “NonEmptyTrimmedString”Migration: semi-auto
v3
import { Schema } from "effect"
const schema = Schema.NonEmptyTrimmedStringv4
import { Schema } from "effect"
const schema = Schema.Trimmed.check(Schema.isNonEmpty())Migration: manual
New imports: SchemaTransformation
v3
import { Schema } from "effect"
const schema = Schema.split(",")v4
import { Schema, SchemaTransformation } from "effect"
function split(separator: string) { return Schema.String.pipe( Schema.decodeTo( Schema.Array(Schema.String), SchemaTransformation.transform({ decode: (s) => s.split(separator) as ReadonlyArray<string>, encode: (as) => as.join(separator) }) ) )}