# Transformations

A transformation connects two schemas with a pair of functions — one that runs
while **decoding** (`Encoded` → `Type`) and one that runs while **encoding**
(`Type` → `Encoded`). This is how a schema can store a date as a string on the
wire yet hand you a real `Date`, or accept `"42"` and give you the number `42`.

```ts
import { Schema, SchemaTransformation } from "effect"

// numberFromString pairs two getters:
//   - decode runs Encoded -> Type   (here: string -> number)
//   - encode runs Type -> Encoded   (here: number -> string)
const NumberFromString = Schema.String.pipe(
  Schema.decodeTo(Schema.Number, SchemaTransformation.numberFromString)
)

console.log(Schema.decodeUnknownSync(NumberFromString)("123")) // => 123 (number)
console.log(Schema.encodeUnknownSync(NumberFromString)(123)) // => "123" (string)
```

Concretely, a `SchemaTransformation.Transformation<T, E>` is just a pair of
`SchemaGetter.Getter`s — `decode: Getter<T, E>` and `encode: Getter<E, T>`. A
`Getter<T, E>` is a single-direction function `Option<E> → Effect<Option<T>, Issue>`
that can fail with a `SchemaIssue`, skip absent keys (`Option.None`), and require
Effect services. The bulk of the work is choosing or writing those two getters.

## decodeTo

`Schema.decodeTo(to, transformation)` is curried: you call it with the target
schema and then pipe the *source* schema into it. The result has
`Encoded = from.Encoded` and `Type = to.Type`.

You can pass either a ready-made `SchemaTransformation`, or a plain object with
`decode`/`encode` getters (which `make` wraps for you).

```ts
import { Schema, SchemaGetter } from "effect"

// Provide the two getters directly:
//   decode: string -> number   encode: number -> string
const NumberFromString = Schema.String.pipe(
  Schema.decodeTo(Schema.Number, {
    decode: SchemaGetter.Number(),
    encode: SchemaGetter.String()
  })
)

console.log(Schema.decodeUnknownSync(NumberFromString)("42")) // => 42
console.log(Schema.encodeUnknownSync(NumberFromString)(42)) // => "42"
```

### Schema.decode (same Type and Encoded)

`Schema.decode` is a convenience for transformations where the encoded and
decoded **types are the same** (e.g. `string` → `string`). Pass a single
`Transformation` or a `{ decode, encode }` getter pair.

```ts
import { Schema, SchemaGetter } from "effect"

// Trim surrounding whitespace while decoding; leave the value alone on encode.
const TrimmedString = Schema.String.pipe(
  Schema.decode({
    decode: SchemaGetter.trim(),
    encode: SchemaGetter.passthrough()
  })
)

console.log(Schema.decodeUnknownSync(TrimmedString)("  hello  ")) // => "hello"
```
**Note:** When the two sides have **different** types, use `Schema.decodeTo` with an
explicit target schema. `Schema.decode` / `Schema.encode` keep `Type === Encoded`.

## Transformations that can fail

Decoding often involves parsing that may not succeed. Use
`SchemaTransformation.transformOrFail` (or `SchemaGetter.transformOrFail` for a
single getter): the function returns an `Effect` and reports failures as a
`SchemaIssue`. The failure is woven into the normal decode error tree, so it
benefits from path tracking and [error formatting](https://effect.plants.sh/schema/error-formatting/).

```ts
import { Effect, Option, Schema, SchemaIssue, SchemaTransformation } from "effect"

// A schema that decodes a string into an integer, failing cleanly on bad input.
const IntFromString = Schema.String.pipe(
  Schema.decodeTo(
    Schema.Number,
    SchemaTransformation.transformOrFail({
      decode: (s) => {
        const n = Number.parseInt(s, 10)
        return Number.isNaN(n)
          ? Effect.fail(
              new SchemaIssue.InvalidValue(Option.some(s), {
                message: "expected an integer string"
              })
            )
          : Effect.succeed(n)
      },
      encode: (n) => Effect.succeed(String(n))
    })
  )
)

console.log(Schema.decodeUnknownSync(IntFromString)("7")) // => 7
// Schema.decodeUnknownSync(IntFromString)("abc") -> throws "expected an integer string"
```

## encodeTo

`Schema.encodeTo` is the mirror image: it lets you name the *encoded* schema
first. `to.pipe(Schema.encodeTo(encodedSchema, { decode, encode }))` reads
naturally when you think of a value being encoded outward. It is otherwise
equivalent to `decodeTo` with the schemas swapped — the `decode` getter still runs
`Encoded → Type`, the `encode` getter still runs `Type → Encoded`.

```ts
import { Schema, SchemaGetter } from "effect"

const NumberFromString = Schema.Number.pipe(
  Schema.encodeTo(Schema.String, {
    decode: SchemaGetter.transform((s: string) => Number(s)),
    encode: SchemaGetter.transform((n: number) => String(n))
  })
)

console.log(Schema.decodeUnknownSync(NumberFromString)("3.14")) // => 3.14
console.log(Schema.encodeUnknownSync(NumberFromString)(3.14)) // => "3.14"
```

`Schema.encode` is the `Type === Encoded` convenience that mirrors `Schema.decode`.

## Composing transformations

Because a transformation produces a regular schema, you can keep piping. A
common pattern is to parse a JSON string and then validate its shape — chaining a
string→JSON transformation into a struct schema.

```ts
import { Schema } from "effect"

const Payload = Schema.Struct({
  id: Schema.String,
  count: Schema.Number
})

// Encoded: a JSON string  ->  Type: the validated Payload object.
const PayloadFromJson = Schema.fromJsonString(Payload)

console.log(Schema.decodeUnknownSync(PayloadFromJson)('{"id":"a","count":3}'))
// => { id: "a", count: 3 }
```

Transformations also compose at the value level: `Transformation#compose` chains
two transformations (decode left-to-right, encode right-to-left), and
`Getter#compose` does the same for individual getters.

```ts
import { SchemaTransformation } from "effect"

// decode: trim then lowercase; encode: passthrough on both
const trimAndLower = SchemaTransformation.trim().compose(
  SchemaTransformation.toLowerCase()
)
```

---

# Authoring transformations: `SchemaTransformation`

The `SchemaTransformation` module builds the bidirectional value that
`decodeTo`/`encodeTo`/`decode`/`encode`/`link` consume. Reach for the built-in
[transforming schemas](#built-in-transforming-schemas) first; drop down to these
constructors when you need a conversion that is not already provided.

### make

Pairs two existing `SchemaGetter.Getter`s into a `Transformation`. Returns its
input unchanged if it is already a `Transformation` (idempotent).

```ts
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))
})
```

### transform

Builds a `Transformation` from pure, infallible decode/encode functions. The
most common constructor.

```ts
import { Schema, SchemaTransformation } from "effect"

const CentsFromDollars = Schema.Number.pipe(
  Schema.decodeTo(
    Schema.Number,
    SchemaTransformation.transform({
      decode: (dollars) => dollars * 100, // => 100 for 1
      encode: (cents) => cents / 100 // => 1 for 100
    })
  )
)

console.log(Schema.decodeUnknownSync(CentsFromDollars)(1)) // => 100
console.log(Schema.encodeUnknownSync(CentsFromDollars)(100)) // => 1
```

### transformOrFail

Builds a `Transformation` from effectful decode/encode functions that may fail
with a `SchemaIssue` or require services. See the
[fallible example above](#transformations-that-can-fail).

```ts
import { Effect, SchemaTransformation } from "effect"

const t = SchemaTransformation.transformOrFail({
  decode: (s: string) => Effect.succeed(s.length),
  encode: (n: number) => Effect.succeed("x".repeat(n))
})
```

### transformOptional

Operates on `Option` values directly, giving full control over missing-key
handling. `Option.None` input means the key is absent; returning `Option.None`
omits the key from output.

```ts
import { Option, Schema, SchemaTransformation } from "effect"

const schema = Schema.Struct({
  a: Schema.optionalKey(Schema.Number).pipe(
    Schema.decodeTo(
      Schema.Option(Schema.Number),
      SchemaTransformation.transformOptional({
        decode: Option.some, // absent -> Some(None), present -> Some(Some(v))
        encode: Option.flatten
      })
    )
  )
})
```

### passthrough / passthroughSupertype / passthroughSubtype

The identity transformation — no conversion in either direction. Used to connect
two schemas that share a runtime type. `passthrough` requires `T === E`; pass
`{ strict: false }` to bypass, or use the typed `passthroughSupertype`
(`T extends E`) / `passthroughSubtype` (`E extends T`) variants.

```ts
import { Schema, SchemaTransformation } from "effect"

const schema = Schema.Trim.pipe(
  Schema.decodeTo(Schema.FiniteFromString, SchemaTransformation.passthrough())
)

// Typed, no runtime change:
const sup = SchemaTransformation.passthroughSupertype<"a" | "b", string>()
const sub = SchemaTransformation.passthroughSubtype<string, "a" | "b">()
```

### String transformation shortcuts

These pair a string getter on decode with `passthrough` on encode (so they are
**lossy** and do not round-trip), except `snakeToCamel` and `splitKeyValue`,
which provide a real inverse.

- **`trim()`** — strips surrounding whitespace on decode.
- **`toLowerCase()`** / **`toUpperCase()`** — case-normalize on decode.
- **`capitalize()`** / **`uncapitalize()`** — change the first character on decode.
- **`snakeToCamel()`** — `"my_field"` ↔ `"myField"` (real inverse on encode).
- **`splitKeyValue(options?)`** — `string` ↔ `Record<string, string>`.

```ts
import { Schema, SchemaTransformation } from "effect"

const Config = Schema.String.pipe(
  Schema.decodeTo(
    Schema.Record(Schema.String, Schema.String),
    SchemaTransformation.splitKeyValue({ separator: ";", keyValueSeparator: ":" })
  )
)

console.log(Schema.decodeUnknownSync(Config)("host:localhost;port:3000"))
// => { host: "localhost", port: "3000" }
```

### isTransformation

Type guard: `true` if a value is a `Transformation` instance.

```ts
import { SchemaTransformation } from "effect"

SchemaTransformation.isTransformation(SchemaTransformation.trim()) // => true
SchemaTransformation.isTransformation({ decode: null }) // => false
```

### Middleware: `middlewareDecoding` / `middlewareEncoding`

A `Transformation` operates on individual values; a `Middleware` wraps the whole
parsing `Effect` for a direction, so it can catch errors, retry, or run side
effects around the pipeline. You usually install one via
`Schema.middlewareDecoding` / `Schema.middlewareEncoding` rather than constructing
`SchemaTransformation.Middleware` by hand.

```ts
import { Effect, Option, Schema } from "effect"

// Recover from a decode failure with a fallback value.
const WithFallback = Schema.String.pipe(
  Schema.middlewareDecoding(
    Effect.catch(() => Effect.succeed(Option.some("fallback")))
  )
)
```

---

# The Getter toolkit: `SchemaGetter`

A `Getter<T, E, R>` is one direction of a transformation. You combine two of them
(decode + encode) in a `decodeTo`/`encodeTo` object, or via
`SchemaTransformation.make`. These are the primitives the built-ins are built from.

### succeed

Always produces a constant, ignoring the input.

```ts
import { SchemaGetter } from "effect"

const alwaysZero = SchemaGetter.succeed(0) // => Some(0) for any input
```

### fail

Always fails with the `Issue` returned by the supplied function.

```ts
import { Option, SchemaGetter, SchemaIssue } from "effect"

const rejectAll = SchemaGetter.fail<string, string>(
  (oe) => new SchemaIssue.InvalidValue(oe, { message: "not allowed" })
)
```

### forbidden

Always fails with a `Forbidden` issue — handy for disabling one direction.

```ts
import { SchemaGetter } from "effect"

const noEncode = SchemaGetter.forbidden<string, number>(() => "encoding is not supported")
```

### transform

Pure, infallible map of present values (`Some(e) → Some(f(e))`, `None` untouched).

```ts
import { SchemaGetter } from "effect"

const double = SchemaGetter.transform<number, number>((n) => n * 2) // => Some(6) for 3
```

### transformOrFail

Fallible/effectful map of present values; propagates the `Issue` on failure.

```ts
import { Effect, Option, SchemaGetter, SchemaIssue } from "effect"

const parseInt = SchemaGetter.transformOrFail<number, string>((s) => {
  const n = Number.parseInt(s, 10)
  return Number.isNaN(n)
    ? Effect.fail(new SchemaIssue.InvalidValue(Option.some(s), { message: "not an integer" }))
    : Effect.succeed(n)
})
```

### transformOptional

Pure transform over the full `Option<E> → Option<T>`, so you can turn present
into absent and vice versa.

```ts
import { Option, SchemaGetter } from "effect"

const skipEmpty = SchemaGetter.transformOptional<string, string>((o) =>
  Option.filter(o, (s) => s.length > 0)
)
```

### onNone / onSome / required

`onNone` supplies a value when the key is absent; `onSome` runs only for present
values; `required` fails with `MissingKey` when absent.

```ts
import { Effect, Option, SchemaGetter } from "effect"

const withTimestamp = SchemaGetter.onNone<number>(() => Effect.succeed(Option.some(Date.now())))
const parseIfPresent = SchemaGetter.onSome<number, string>((s) => Effect.succeed(Option.some(Number(s))))
const mustExist = SchemaGetter.required<string>() // => fails MissingKey when absent
```

### withDefault

Replaces `Some(undefined)` / `None` with a default produced by an `Effect`.

```ts
import { Effect, SchemaGetter } from "effect"

const withZero = SchemaGetter.withDefault(Effect.succeed(0)) // Getter<number, number | undefined>
```
**Tip:** For declaring defaults at the field level, see
[default constructors](https://effect.plants.sh/schema/default-constructors/).

### omit

Always returns `None`, dropping the value from output.

```ts
import { SchemaGetter } from "effect"

const omitField = SchemaGetter.omit<string>() // => None
```

### checkEffect

Validates a present value with an effectful check, without changing it. Return
`undefined`/`true` to accept; `false`, a `string`, or an `Issue` to reject.

```ts
import { Effect, SchemaGetter } from "effect"

const nonNegative = SchemaGetter.checkEffect<number>((n) =>
  Effect.succeed(n >= 0 ? undefined : "must be non-negative")
)
```

### Value coercions: String / Number / Boolean / BigInt / Date

Pure coercions delegating to the global constructors. `Number` may yield `NaN`;
`BigInt` and `Date` may throw / produce invalid values for bad input.

```ts
import { SchemaGetter } from "effect"

SchemaGetter.String<number>() // Getter<string, number>  e.g. 42 => "42"
SchemaGetter.Number<string>() // Getter<number, string>  e.g. "42" => 42
SchemaGetter.Boolean<string>() // Getter<boolean, string> e.g. "" => false
SchemaGetter.BigInt<string>() // Getter<bigint, string>  e.g. "9" => 9n
SchemaGetter.Date<string>() // Getter<Date, string>    e.g. "2020-01-01" => Date
```

### String getters

- **`trim()`**, **`capitalize()`**, **`uncapitalize()`**, **`toLowerCase()`**, **`toUpperCase()`** — pure string ops.
- **`snakeToCamel()`** / **`camelToSnake()`** — case-convention conversions (inverses).
- **`split(options?)`** — `"a,b,c"` → `["a", "b", "c"]` (empty string → `[]`).
- **`splitKeyValue(options?)`** / **`joinKeyValue(options?)`** — string ↔ `Record<string, string>` (inverses).

```ts
import { SchemaGetter } from "effect"

const toCamel = SchemaGetter.snakeToCamel<string>() // "my_field" => "myField"
const split = SchemaGetter.split<string>() // "a,b" => ["a", "b"]
const parse = SchemaGetter.splitKeyValue<string>() // "a=1,b=2" => { a: "1", b: "2" }
const join = SchemaGetter.joinKeyValue() // { a: "1", b: "2" } => "a=1,b=2"
```

### JSON getters: parseJson / stringifyJson

`parseJson` parses a JSON string (fails with `InvalidValue` on bad JSON; accepts
an optional `reviver`); `stringifyJson` serializes (optional `replacer`/`space`).

```ts
import { SchemaGetter } from "effect"

const parse = SchemaGetter.parseJson<string>() // '{"a":1}' => { a: 1 }
const stringify = SchemaGetter.stringifyJson() // { a: 1 } => '{"a":1}'
```

### Encoding getters: Base64, Hex, URI component

Decode getters fail with `InvalidValue` on malformed input. Each has a string and
a `Uint8Array` flavor.

- **`encodeBase64()`** / **`decodeBase64()`** / **`decodeBase64String()`** — bytes/string ↔ Base64.
- **`encodeBase64Url()`** / **`decodeBase64Url()`** / **`decodeBase64UrlString()`** — URL-safe Base64.
- **`encodeHex()`** / **`decodeHex()`** / **`decodeHexString()`** — bytes/string ↔ hex.
- **`encodeUriComponent()`** / **`decodeUriComponent()`** — `encodeURIComponent` / `decodeURIComponent`.

```ts
import { SchemaGetter } from "effect"

const enc = SchemaGetter.encodeBase64<string>() // "hi" => "aGk="
const dec = SchemaGetter.decodeBase64String<string>() // "aGk=" => "hi"
const hex = SchemaGetter.encodeHex<string>() // "hi" => "6869"
const uri = SchemaGetter.encodeUriComponent<string>() // "a b" => "a%20b"
```

### DateTime getter: dateTimeUtcFromInput

Parses a `DateTime.Input` (string, epoch millis, `Date`, parts, …) into a
`DateTime.Utc`; fails with `InvalidValue` on unparseable input.

```ts
import { SchemaGetter } from "effect"

const parseDate = SchemaGetter.dateTimeUtcFromInput<string>() // Getter<DateTime.Utc, string>
```

### Web getters: FormData / URLSearchParams

Decode parses bracket-path keys (`user[name]`, `items[0]`) into a nested record;
encode flattens a nested object back into bracket-path entries.

- **`decodeFormData()`** / **`encodeFormData()`**
- **`decodeURLSearchParams()`** / **`encodeURLSearchParams()`**
- **`makeTreeRecord(entries)`** / **`collectBracketPathEntries(isLeaf)`** — the underlying parse/flatten helpers.

```ts
import { SchemaGetter } from "effect"

SchemaGetter.makeTreeRecord([
  ["user[name]", "Alice"],
  ["user[tags][]", "admin"],
  ["user[tags][]", "editor"]
])
// => { user: { name: "Alice", tags: ["admin", "editor"] } }
```

---

# Built-in transforming schemas

These ship ready to use, so you rarely write getters by hand. Every entry below
shows **both directions** with inline `// =>` results.
**Tip:** New in Effect v4: the **Duration** family (`Duration`, `DurationFromString`,
`DurationFromMillis`, `DurationFromNanos`), the string/binary **encoding** schemas
(`StringFromBase64`, `StringFromBase64Url`, `StringFromHex`,
`StringFromUriComponent`), and **`BigDecimalFromString`**.

## Strings ↔ numbers and bigints

### NumberFromString

`string` ↔ `number`, allowing non-finite results like `NaN` / `Infinity`.

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.NumberFromString)("3.14") // => 3.14
Schema.encodeUnknownSync(Schema.NumberFromString)(3.14) // => "3.14"
```

### FiniteFromString

Like `NumberFromString` but rejects `NaN`, `Infinity`, and `-Infinity` on decode.

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.FiniteFromString)("42") // => 42
// Schema.decodeUnknownSync(Schema.FiniteFromString)("Infinity") -> throws
```

### BigIntFromString

`string` ↔ `bigint`. Decoding accepts only strings matching `^-?\d+$`.

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.BigIntFromString)("9007199254740993") // => 9007199254740993n
Schema.encodeUnknownSync(Schema.BigIntFromString)(9007199254740993n) // => "9007199254740993"
```

### isStringBigInt

The string filter used by `BigIntFromString` — checks that a `string` parses as a
bigint. Apply it with `Schema.check`.

```ts
import { Schema } from "effect"

const BigIntString = Schema.String.check(Schema.isStringBigInt())
Schema.decodeUnknownSync(BigIntString)("123") // => "123"
// Schema.decodeUnknownSync(BigIntString)("1.5") -> throws
```

### BigDecimalFromString

`string` ↔ `BigDecimal` for arbitrary-precision decimals. (v4-new)

```ts
import { Schema } from "effect"

const bd = Schema.decodeUnknownSync(Schema.BigDecimalFromString)("1.5") // => BigDecimal 1.5
Schema.encodeUnknownSync(Schema.BigDecimalFromString)(bd) // => "1.5"
```

### BooleanFromBit

`0 | 1` ↔ `boolean`.

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.BooleanFromBit)(1) // => true
Schema.encodeUnknownSync(Schema.BooleanFromBit)(false) // => 0
```

## Dates and times

### DateFromString

`string` ↔ `Date` (via `new Date` / `toISOString`). No validity check; pair with
checks if you need one.

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.DateFromString)("2020-01-01T00:00:00.000Z") // => Date
Schema.encodeUnknownSync(Schema.DateFromString)(new Date(0)) // => "1970-01-01T00:00:00.000Z"
```

### DateTimeUtc

A declaration schema for `DateTime.Utc` values (no transformation; validates that
the input is already a `DateTime.Utc`). The building block for the codecs below.

```ts
import { DateTime, Schema } from "effect"

Schema.decodeUnknownSync(Schema.DateTimeUtc)(DateTime.makeUnsafe(0)) // => DateTime.Utc
```

### DateTimeUtcFromString

`string` (ISO) ↔ `DateTime.Utc`; encodes with `DateTime.formatIso`.

```ts
import { Schema } from "effect"

const dt = Schema.decodeUnknownSync(Schema.DateTimeUtcFromString)("2020-01-01T00:00:00Z") // => DateTime.Utc
Schema.encodeUnknownSync(Schema.DateTimeUtcFromString)(dt) // => "2020-01-01T00:00:00.000Z"
```

### DateTimeUtcFromDate

`Date` ↔ `DateTime.Utc`.

```ts
import { Schema } from "effect"

const dt = Schema.decodeUnknownSync(Schema.DateTimeUtcFromDate)(new Date(0)) // => DateTime.Utc
Schema.encodeUnknownSync(Schema.DateTimeUtcFromDate)(dt) // => Date(1970-01-01T00:00:00.000Z)
```

### DateTimeUtcFromMillis

`number` (epoch millis) ↔ `DateTime.Utc`.

```ts
import { Schema } from "effect"

const dt = Schema.decodeUnknownSync(Schema.DateTimeUtcFromMillis)(0) // => DateTime.Utc (epoch)
Schema.encodeUnknownSync(Schema.DateTimeUtcFromMillis)(dt) // => 0
```

### DateTimeZoned

Declaration schema for `DateTime.Zoned` values (validates the input is zoned).

```ts
import { Schema } from "effect"

// Decoding requires a DateTime.Zoned value; pairs with DateTimeZonedFromString below.
const schema = Schema.DateTimeZoned
```

### DateTimeZonedFromString

ISO zoned string ↔ `DateTime.Zoned`; encodes with `DateTime.formatIsoZoned`.

```ts
import { Schema } from "effect"

const z = Schema.decodeUnknownSync(Schema.DateTimeZonedFromString)(
  "2020-01-01T00:00:00.000Z[Europe/London]"
) // => DateTime.Zoned
Schema.encodeUnknownSync(Schema.DateTimeZonedFromString)(z)
// => "2020-01-01T00:00:00.000Z[Europe/London]"
```

### TimeZone / TimeZoneFromString

`TimeZone` is the declaration schema for any `DateTime.TimeZone`;
`TimeZoneFromString` decodes either an IANA id or an offset string (e.g.
`"+03:00"`) into one.

```ts
import { Schema } from "effect"

const tz = Schema.decodeUnknownSync(Schema.TimeZoneFromString)("Europe/London") // => TimeZone
Schema.encodeUnknownSync(Schema.TimeZoneFromString)(tz) // => "Europe/London"
```

### TimeZoneNamed / TimeZoneNamedFromString

`TimeZoneNamed` declares a `DateTime.TimeZone.Named`; `TimeZoneNamedFromString`
decodes only valid IANA identifier strings into one.

```ts
import { Schema } from "effect"

const named = Schema.decodeUnknownSync(Schema.TimeZoneNamedFromString)("America/New_York") // => Named
Schema.encodeUnknownSync(Schema.TimeZoneNamedFromString)(named) // => "America/New_York"
```

### TimeZoneOffset

Declaration schema for a fixed `DateTime.TimeZone.Offset`. Use
`SchemaTransformation.timeZoneOffsetFromNumber` to build a `number` ↔ offset codec.

```ts
import { Schema } from "effect"

const schema = Schema.TimeZoneOffset // validates DateTime.TimeZone.Offset values
```

## Durations (v4-new)

### Duration

Declaration schema for `Duration` values (no conversion; validates a `Duration`).

```ts
import { Duration, Schema } from "effect"

Schema.decodeUnknownSync(Schema.Duration)(Duration.seconds(5)) // => Duration(5s)
```

### DurationFromString

Human-readable `string` ↔ `Duration` (e.g. `"2 seconds"`, `"10 nanos"`,
`"Infinity"`). (v4-new)

```ts
import { Schema } from "effect"

const d = Schema.decodeUnknownSync(Schema.DurationFromString)("2 seconds") // => Duration(2s)
Schema.encodeUnknownSync(Schema.DurationFromString)(d) // => "2 seconds"
```

### DurationFromMillis

`number` (millis) ↔ `Duration`.

```ts
import { Schema } from "effect"

const d = Schema.decodeUnknownSync(Schema.DurationFromMillis)(2000) // => Duration(2s)
Schema.encodeUnknownSync(Schema.DurationFromMillis)(d) // => 2000
```

### DurationFromNanos

`bigint` (nanos) ↔ `Duration`. Encode fails if the `Duration` is not
bigint-representable (e.g. `Duration.infinity`).

```ts
import { Schema } from "effect"

const d = Schema.decodeUnknownSync(Schema.DurationFromNanos)(2_000_000_000n) // => Duration(2s)
Schema.encodeUnknownSync(Schema.DurationFromNanos)(d) // => 2000000000n
```

## Binary encodings (v4-new)

### StringFromBase64 / StringFromBase64Url / StringFromHex

Base64 (or URL-safe Base64, or hex) `string` ↔ UTF-8 `string`. Decode fails on
malformed input.

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.StringFromBase64)("aGVsbG8=") // => "hello"
Schema.encodeUnknownSync(Schema.StringFromBase64)("hello") // => "aGVsbG8="

Schema.decodeUnknownSync(Schema.StringFromHex)("6869") // => "hi"
Schema.encodeUnknownSync(Schema.StringFromHex)("hi") // => "6869"
```

### StringFromUriComponent

URI-component-encoded `string` ↔ decoded `string`. (v4-new)

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.StringFromUriComponent)("a%20b") // => "a b"
Schema.encodeUnknownSync(Schema.StringFromUriComponent)("a b") // => "a%20b"
```

### Uint8ArrayFromBase64 / Uint8ArrayFromBase64Url / Uint8ArrayFromHex

Encoded `string` ↔ `Uint8Array` for binary payloads.

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.Uint8ArrayFromBase64)("aGk=") // => Uint8Array [104, 105]
Schema.encodeUnknownSync(Schema.Uint8ArrayFromBase64)(new Uint8Array([104, 105])) // => "aGk="

Schema.decodeUnknownSync(Schema.Uint8ArrayFromHex)("6869") // => Uint8Array [104, 105]
Schema.encodeUnknownSync(Schema.Uint8ArrayFromHex)(new Uint8Array([104, 105])) // => "6869"
```

## URLs

### URL / URLFromString

`URL` is the declaration schema for `globalThis.URL` instances; `URLFromString`
decodes a `string` into a `URL` (fails on invalid URLs) and encodes back to
`url.href`.

```ts
import { Schema } from "effect"

const url = Schema.decodeUnknownSync(Schema.URLFromString)("https://effect.website/docs") // => URL
Schema.encodeUnknownSync(Schema.URLFromString)(url) // => "https://effect.website/docs"
```

## JSON and wire formats

### fromJsonString

Parses a JSON `string` and decodes the result with the provided schema; encodes
by validating then `JSON.stringify`-ing.

```ts
import { Schema } from "effect"

const Ids = Schema.fromJsonString(Schema.Array(Schema.Number))
Schema.decodeUnknownSync(Ids)("[1,2,3]") // => [1, 2, 3]
Schema.encodeUnknownSync(Ids)([1, 2, 3]) // => "[1,2,3]"
```

### UnknownFromJsonString

`fromJsonString(Schema.Unknown)` — parse arbitrary JSON without a target shape.

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.UnknownFromJsonString)('{"ok":true}') // => { ok: true }
Schema.encodeUnknownSync(Schema.UnknownFromJsonString)({ ok: true }) // => '{"ok":true}'
```

### fromFormData

`FormData` ↔ a value of the given schema, using bracket-path keys
(`user[name]`) for nesting.

```ts
import { Schema } from "effect"

const Form = Schema.fromFormData(Schema.Struct({ name: Schema.String }))
const fd = new FormData()
fd.append("name", "Alice")
Schema.decodeUnknownSync(Form)(fd) // => { name: "Alice" }
```

### fromURLSearchParams

`URLSearchParams` ↔ a value of the given schema, also via bracket-path keys.

```ts
import { Schema } from "effect"

const Query = Schema.fromURLSearchParams(Schema.Struct({ page: Schema.String }))
Schema.decodeUnknownSync(Query)(new URLSearchParams("page=2")) // => { page: "2" }
```

### Trim / Trimmed

`Trimmed` validates that a `string` has no surrounding whitespace; `Trim`
*decodes* by trimming (encode is passthrough, so it does not round-trip).

```ts
import { Schema } from "effect"

Schema.decodeUnknownSync(Schema.Trim)("  hi  ") // => "hi"
Schema.decodeUnknownSync(Schema.Trimmed)("hi") // => "hi"
// Schema.decodeUnknownSync(Schema.Trimmed)("  hi  ") -> throws
```

## Option transformations

Each turns a nullable/optional encoded shape into an `Option<T>` on the decoded
side. They take the inner schema and return a `decodeTo` of `Option<inner>`.

### OptionFromNullOr

`T | null` ↔ `Option<T>`.

```ts
import { Schema } from "effect"

const schema = Schema.OptionFromNullOr(Schema.String)
Schema.decodeUnknownSync(schema)(null) // => Option.none()
Schema.encodeUnknownSync(schema)(Schema.decodeUnknownSync(schema)("a")) // => "a"
```

### OptionFromUndefinedOr

`T | undefined` ↔ `Option<T>`.

```ts
import { Schema } from "effect"

const schema = Schema.OptionFromUndefinedOr(Schema.Number)
Schema.decodeUnknownSync(schema)(undefined) // => Option.none()
Schema.decodeUnknownSync(schema)(5) // => Option.some(5)
```

### OptionFromNullishOr

`T | null | undefined` ↔ `Option<T>`. Choose how `None` encodes via
`onNoneEncoding`.

```ts
import { Schema } from "effect"

const schema = Schema.OptionFromNullishOr(Schema.String, { onNoneEncoding: null })
Schema.decodeUnknownSync(schema)(null) // => Option.none()
Schema.decodeUnknownSync(schema)("a") // => Option.some("a")
```

### OptionFromOptional

An optional (possibly-`undefined`) field ↔ `Option<T>`.

```ts
import { Schema } from "effect"

const schema = Schema.Struct({
  age: Schema.OptionFromOptional(Schema.Number)
})
Schema.decodeUnknownSync(schema)({}) // => { age: Option.none() }
Schema.decodeUnknownSync(schema)({ age: 30 }) // => { age: Option.some(30) }
```

### OptionFromOptionalKey

An optional struct *key* ↔ `Option<T>`; absent key decodes to `Option.none()`,
and `Option.none()` encodes by omitting the key.

```ts
import { Schema } from "effect"

const schema = Schema.Struct({
  name: Schema.OptionFromOptionalKey(Schema.String)
})
Schema.decodeUnknownSync(schema)({}) // => { name: Option.none() }
Schema.decodeUnknownSync(schema)({ name: "Al" }) // => { name: Option.some("Al") }
```

### OptionFromOptionalNullOr

An optional key whose value may also be `null` ↔ `Option<T>`.

```ts
import { Schema } from "effect"

const schema = Schema.Struct({
  bio: Schema.OptionFromOptionalNullOr(Schema.String)
})
Schema.decodeUnknownSync(schema)({ bio: null }) // => { bio: Option.none() }
Schema.decodeUnknownSync(schema)({}) // => { bio: Option.none() }
Schema.decodeUnknownSync(schema)({ bio: "hi" }) // => { bio: Option.some("hi") }
```

## Redacted

### Redacted

Wraps a schema so its value is hidden from error output and inspection. Expects
the input to already be a `Redacted` instance.

```ts
import { Redacted, Schema } from "effect"

const Secret = Schema.Redacted(Schema.String)
Schema.decodeUnknownSync(Secret)(Redacted.make("p@ss")) // => Redacted(<redacted>)
```

### RedactedFromValue

Decodes the *raw* value and wraps it in `Redacted<T>` (encode unwraps, unless
`disallowEncode` is set).

```ts
import { Schema } from "effect"

const Secret = Schema.RedactedFromValue(Schema.String)
const r = Schema.decodeUnknownSync(Secret)("p@ss") // => Redacted(<redacted>)
Schema.encodeUnknownSync(Secret)(r) // => "p@ss"
```

### redact

The decoding middleware behind `Redacted` — wraps decode errors so schema details
do not leak in messages. Apply it to any schema.

```ts
import { Schema } from "effect"

const SafeNumber = Schema.redact(Schema.Number) // errors are redacted on failure
```

## Structural transforms

### flip

Swaps `Type` and `Encoded`, turning a decoder into its inverse. Calling `flip`
twice returns the original.

```ts
import { Schema } from "effect"

const StringFromNumber = Schema.flip(Schema.NumberFromString)
Schema.decodeUnknownSync(StringFromNumber)(42) // => "42"
Schema.encodeUnknownSync(StringFromNumber)("42") // => 42
```

### toType / toEncoded

`toType` collapses a schema to just its decoded (`Type`) side; `toEncoded`
collapses it to its `Encoded` side — both drop the transformation.

```ts
import { Schema } from "effect"

const TypeOnly = Schema.toType(Schema.NumberFromString) // schema for `number`
const EncodedOnly = Schema.toEncoded(Schema.NumberFromString) // schema for `string`
Schema.decodeUnknownSync(TypeOnly)(1) // => 1
Schema.decodeUnknownSync(EncodedOnly)("1") // => "1"
```

### encodeKeys

Renames struct keys on the encoded side, keeping the decoded property names.

```ts
import { Schema } from "effect"

const Person = Schema.Struct({
  firstName: Schema.String
}).pipe(Schema.encodeKeys({ firstName: "first_name" }))

Schema.decodeUnknownSync(Person)({ first_name: "Al" }) // => { firstName: "Al" }
Schema.encodeUnknownSync(Person)({ firstName: "Al" }) // => { first_name: "Al" }
```

### mutable

Drops `readonly` from a struct/array/record's decoded type (the runtime value is
unchanged).

```ts
import { Schema } from "effect"

const Items = Schema.mutable(Schema.Array(Schema.Number)) // Type: number[] (not readonly)
Schema.decodeUnknownSync(Items)([1, 2]) // => [1, 2]
```

### link

Used inside custom `declare` schemas to attach a transformation to a target type
(this is how built-ins like `Redacted` define their JSON codec). Reach for
`decodeTo`/`encodeTo` in everyday code; use `link` when authoring a declaration
schema's serializer.

```ts
import { Schema, SchemaGetter } from "effect"

// Sketch: link a target type to a transformation pair.
const toCodec = Schema.link<string>()(Schema.String, {
  decode: SchemaGetter.passthrough(),
  encode: SchemaGetter.passthrough()
})
```
**Note:** For shaping the failures these transformations produce, see
[error formatting](https://effect.plants.sh/schema/error-formatting/). To attach default values to
optional fields, see [default constructors](https://effect.plants.sh/schema/default-constructors/).