# Primitives, Literals & Unions

The leaves of every schema are primitives. Effect ships schemas for each
JavaScript primitive, for individual literal values, for common built-in classes
(`Date`, `URL`, `RegExp`, ...), and for the special top/bottom types. You combine
these — and the structs in the next pages — into larger shapes.

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

// A single description reusing primitive and literal schemas.
const Account = Schema.Struct({
  id: Schema.String,
  balance: Schema.Number,
  active: Schema.Boolean,
  // A union of literals: only these three strings are accepted.
  tier: Schema.Literals(["free", "pro", "enterprise"])
})

const account = Schema.decodeUnknownSync(Account)({
  id: "acc_1",
  balance: 100,
  active: true,
  tier: "pro"
})

console.log(account.tier) // => "pro"
```

Every schema on this page is **non-transforming**: its decoded `Type` equals its
encoded `Encoded`, so decoding and encoding pass values through unchanged (they
only validate). Schemas that convert between representations — `NumberFromString`,
`DateFromString`, the `*FromString` family, `Option`/`Map`/`Set` codecs — live in
[transformations](https://effect.plants.sh/schema/transformations/).

## The common primitives

These are the schemas you reach for constantly. Each decodes from and encodes to
the same primitive type.

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

Schema.decodeUnknownSync(Schema.String)("hi") // => "hi"
Schema.decodeUnknownSync(Schema.Number)(42) // => 42
Schema.decodeUnknownSync(Schema.Boolean)(true) // => true
Schema.decodeUnknownSync(Schema.BigInt)(10n) // => 10n
```

For "this value, or `null`/`undefined`", combine a primitive with a dedicated
combinator instead of writing the union by hand:

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

const Nickname = Schema.NullOr(Schema.String) // string | null
const Bio = Schema.UndefinedOr(Schema.String) // string | undefined
const Avatar = Schema.NullishOr(Schema.String) // string | null | undefined
```

For a closed set of string constants (a "string enum"), use `Schema.Literals`:

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

const Status = Schema.Literals(["active", "inactive", "pending"])
type Status = typeof Status.Type // "active" | "inactive" | "pending"
```

---

The rest of this page is the exhaustive catalogue: every built-in primitive,
literal/enum/template-literal constructor, and union combinator, each with a short
runnable example.

## Primitive schemas

Each schema below decodes from and encodes to the matching JavaScript primitive.

### Schema.String

Accepts any `string`.

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

Schema.decodeUnknownSync(Schema.String)("hello") // => "hello"
```

### Schema.Number

Accepts any `number`, **including** `NaN`, `Infinity`, and `-Infinity`. Use
[`Finite`](#schemafinite) or [`Int`](#schemaint) to exclude those.

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

Schema.decodeUnknownSync(Schema.Number)(3.14) // => 3.14
Schema.decodeUnknownSync(Schema.Number)(NaN) // => NaN (accepted)
```

### Schema.Boolean

Accepts `true` or `false`.

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

Schema.decodeUnknownSync(Schema.Boolean)(false) // => false
```

### Schema.BigInt

Accepts any `bigint`. (The string codec `BigIntFromString` lives in
[transformations](https://effect.plants.sh/schema/transformations/).)

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

Schema.decodeUnknownSync(Schema.BigInt)(123n) // => 123n
```

### Schema.Symbol

Accepts any `symbol`. For one specific symbol, use
[`UniqueSymbol`](#schemauniquesymbolsymbol).

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

Schema.decodeUnknownSync(Schema.Symbol)(Symbol.for("x")) // => Symbol(x)
```

### Schema.Null

Accepts only `null`.

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

Schema.decodeUnknownSync(Schema.Null)(null) // => null
```

### Schema.Undefined

Accepts only `undefined`.

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

Schema.decodeUnknownSync(Schema.Undefined)(undefined) // => undefined
```

### Schema.Void

A schema whose `Type` is `void`. Useful for the return position of effects/RPCs
that produce no meaningful value.

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

Schema.decodeUnknownSync(Schema.Void)(undefined) // => undefined
```

### Schema.ObjectKeyword

Accepts any non-null `object` (typed as `object`) — arrays and functions
included, but not primitives. This is the schema for the TypeScript `object`
keyword; for actual struct shapes use `Schema.Struct` (see
[structs and records](https://effect.plants.sh/schema/structs-and-records/)).

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

Schema.decodeUnknownSync(Schema.ObjectKeyword)({ a: 1 }) // => { a: 1 }
Schema.decodeUnknownSync(Schema.ObjectKeyword)([1, 2]) // => [1, 2]
// Schema.decodeUnknownSync(Schema.ObjectKeyword)("x") -> throws
```

### Schema.PropertyKey

A union of the value types usable as object keys: finite `number`, `symbol`, or
`string` (`Schema.Union([Schema.Finite, Schema.Symbol, Schema.String])`).

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

Schema.decodeUnknownSync(Schema.PropertyKey)("key") // => "key"
Schema.decodeUnknownSync(Schema.PropertyKey)(0) // => 0
```

## Top and bottom types

These describe the extreme ends of the type lattice. Use `Unknown`/`Any`
deliberately — they opt that position out of validation.

### Schema.Unknown

Accepts any value, typed as `unknown`. Validation defers to wherever the value is
later narrowed.

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

Schema.decodeUnknownSync(Schema.Unknown)({ anything: true }) // => { anything: true }
```

### Schema.Any

Accepts any value, typed as `any`. Same runtime behaviour as `Unknown` but with
TypeScript's escape-hatch type — prefer `Unknown`.

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

Schema.decodeUnknownSync(Schema.Any)(42) // => 42
```

### Schema.Never

Accepts nothing — decoding always fails. Useful as the "impossible" position in
conditional types and as a placeholder.

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

// Schema.decodeUnknownSync(Schema.Never)(1) -> throws (no value is valid)
```

## Refined primitives (still non-transforming)

These attach a built-in [filter](https://effect.plants.sh/schema/filters/) to a base primitive. They
validate more strictly but do **not** change the type — decode/encode still pass
the value through.

### Schema.Finite

`Schema.Number` that rejects `NaN`, `Infinity`, and `-Infinity`.

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

Schema.decodeUnknownSync(Schema.Finite)(3.14) // => 3.14
// Schema.decodeUnknownSync(Schema.Finite)(Infinity) -> throws
```

### Schema.Int

`Schema.Number` that requires an integer (also rejects `NaN`/`Infinity`).

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

Schema.decodeUnknownSync(Schema.Int)(7) // => 7
// Schema.decodeUnknownSync(Schema.Int)(7.5) -> throws
```

### Schema.NonEmptyString

`Schema.String` with at least one character.

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

Schema.decodeUnknownSync(Schema.NonEmptyString)("a") // => "a"
// Schema.decodeUnknownSync(Schema.NonEmptyString)("") -> throws
```

### Schema.Char

`Schema.String` whose JavaScript `.length` is exactly `1`. Note this counts
UTF-16 code units, so some single visible glyphs (emoji, combined characters) do
not qualify.

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

Schema.decodeUnknownSync(Schema.Char)("x") // => "x"
// Schema.decodeUnknownSync(Schema.Char)("xy") -> throws
```

## Built-in class schemas (`instanceof`)

These validate that a value is an instance of a well-known JavaScript class. The
decoded `Type` is the class instance and decoding passes it through unchanged. (A
default JSON serializer is attached for each — see the note per schema — but that
only matters when you go through the JSON codec layer.)

### Schema.Date

Accepts any `Date` instance, **including invalid dates** like
`new Date("nope")`. Use [`DateValid`](#schemadatevalid) to reject those, or
`Schema.DateFromString` (in [transformations](https://effect.plants.sh/schema/transformations/)) to decode
from an ISO string.

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

const d = new Date("2024-01-01")
Schema.decodeUnknownSync(Schema.Date)(d) // => Date 2024-01-01T00:00:00.000Z
// Schema.decodeUnknownSync(Schema.Date)("2024-01-01") -> throws (string, not a Date)
```

### Schema.DateValid

`Schema.Date` that additionally rejects invalid `Date` instances.

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

Schema.decodeUnknownSync(Schema.DateValid)(new Date("2024-01-01")) // => Date ...
// Schema.decodeUnknownSync(Schema.DateValid)(new Date("nope")) -> throws
```

### Schema.RegExp

Accepts any `RegExp` instance. The JSON serializer encodes it as
`{ source, flags }`.

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

Schema.decodeUnknownSync(Schema.RegExp)(/ab+c/i) // => /ab+c/i
```

### Schema.URL

Accepts any `URL` instance. The JSON serializer encodes it as a string. To decode
a URL *from* a string, use `Schema.URLFromString`
([transformations](https://effect.plants.sh/schema/transformations/)).

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

Schema.decodeUnknownSync(Schema.URL)(new URL("https://effect.website")) // => URL ...
```

### Schema.Uint8Array

Accepts any `Uint8Array`. The JSON serializer encodes it as a Base64 string.

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

Schema.decodeUnknownSync(Schema.Uint8Array)(new Uint8Array([1, 2, 3])) // => Uint8Array(3) [1, 2, 3]
```

### Schema.File

Accepts any `File` instance. The JSON serializer encodes it as
`{ data, type, name, lastModified }` with `data` Base64-encoded.

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

const file = new File(["hi"], "greeting.txt", { type: "text/plain" })
Schema.decodeUnknownSync(Schema.File)(file) // => File ...
```

### Schema.FormData

Accepts any `FormData` instance. The JSON serializer encodes it as an array of
`[key, entry]` pairs.

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

const form = new FormData()
form.append("name", "Ada")
Schema.decodeUnknownSync(Schema.FormData)(form) // => FormData ...
```

### Schema.URLSearchParams

Accepts any `URLSearchParams` instance. The JSON serializer encodes it as a query
string.

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

Schema.decodeUnknownSync(Schema.URLSearchParams)(
  new URLSearchParams("page=1&limit=20")
) // => URLSearchParams { 'page' => '1', 'limit' => '20' }
```

### Schema.instanceOf

Build an `instanceof` schema for **any** class, including your own. Decoding and
encoding pass the value through unchanged.

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

class Point {
  constructor(readonly x: number, readonly y: number) {}
}

const PointSchema = Schema.instanceOf(Point)

Schema.decodeUnknownSync(PointSchema)(new Point(1, 2)) // => Point { x: 1, y: 2 }
// Schema.decodeUnknownSync(PointSchema)({ x: 1, y: 2 }) -> throws (not a Point)
```

## JSON values

### Schema.Json

Accepts any immutable JSON-compatible value: `null`, `number`, `boolean`,
`string`, readonly arrays of `Json`, or readonly string-keyed records of `Json`.

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

const r = Schema.decodeUnknownOption(Schema.Json)({ key: [1, true, null] })
console.log(r._tag) // => "Some"
```

### Schema.MutableJson

Like [`Json`](#schemajson) but the arrays and records in its type are mutable.

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

Schema.decodeUnknownSync(Schema.MutableJson)([1, "two", false]) // => [1, "two", false]
```

## Literals & enums

### Schema.Literal

Matches one exact value. Accepts a single string, number, bigint, boolean, or
`null`.

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

const Yes = Schema.Literal("yes")
Schema.decodeUnknownSync(Yes)("yes") // => "yes"
// Schema.decodeUnknownSync(Yes)("no") -> throws

// Read the literal back off the schema:
Yes.literal // => "yes"
```

### Schema.Literals

A union of literal values — the idiomatic way to model a closed set of constants.
The members are tried in order; `.pick` narrows to a subset.

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

const Status = Schema.Literals(["active", "inactive", "pending"])
type Status = typeof Status.Type // "active" | "inactive" | "pending"

Schema.decodeUnknownSync(Status)("active") // => "active"

// Narrow to a subset, preserving the literal type:
const Live = Status.pick(["active", "pending"])
type Live = typeof Live.Type // "active" | "pending"

// Access the underlying literal array and member schemas:
Status.literals // => ["active", "inactive", "pending"]
```

### Schema.Enum

Builds a schema from a TypeScript `enum`, accepting any of its values.

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

enum Direction {
  Up = "UP",
  Down = "DOWN"
}

const DirectionSchema = Schema.Enum(Direction)

Schema.decodeUnknownSync(DirectionSchema)(Direction.Up) // => "UP"
Schema.decodeUnknownSync(DirectionSchema)("DOWN") // => "DOWN"
// Schema.decodeUnknownSync(DirectionSchema)("LEFT") -> throws

DirectionSchema.enums // => the original enum object
```

### Schema.UniqueSymbol

Matches one specific `symbol` (and only that symbol). For any symbol, use
[`Symbol`](#schemasymbol).

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

const Brand = Symbol.for("Brand")
const BrandSchema = Schema.UniqueSymbol(Brand)

Schema.decodeUnknownSync(BrandSchema)(Brand) // => Symbol(Brand)
// Schema.decodeUnknownSync(BrandSchema)(Symbol.for("Other")) -> throws
```

## Template literals

### Schema.TemplateLiteral

Validates strings matching a template-literal pattern. Each part is either a
literal `string`/`number`/`bigint` or a schema whose **encoded** type is a string,
number, or bigint. The value stays a string — this only validates the shape.

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

const Route = Schema.TemplateLiteral(["/user/", Schema.Number])
// Type: `/user/${number}`

Schema.decodeUnknownSync(Route)("/user/123") // => "/user/123"
// Schema.decodeUnknownSync(Route)("/user/abc") -> throws

Route.parts // => ["/user/", Schema.Number]
```

### Schema.TemplateLiteralParser

Same matching as `TemplateLiteral`, but **decodes** the matched parts into a
readonly tuple (one element per part). Literal parts contribute their literal
value; schema parts contribute their decoded `Type` — so use a decoding schema
like `Schema.NumberFromString` to actually convert a captured segment.

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

const Route = Schema.TemplateLiteralParser(["/user/", Schema.NumberFromString])

// Decode: string -> typed tuple
Schema.decodeUnknownSync(Route)("/user/42") // => ["/user/", 42]

// Encode: tuple -> string
Schema.encodeSync(Route)(["/user/", 42]) // => "/user/42"
```

## Unions

### Schema.Union

Accepts a value matching any member. By default (`mode: "anyOf"`) members are
tried in order and the first match wins; pass `mode: "oneOf"` to require that
exactly one member matches.

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

const StringOrNumber = Schema.Union([Schema.String, Schema.Number])

Schema.decodeUnknownSync(StringOrNumber)("hello") // => "hello"
Schema.decodeUnknownSync(StringOrNumber)(42) // => 42

// Access / transform members:
StringOrNumber.members // => [Schema.String, Schema.Number]
```

### Schema.NullOr

`Schema.Union([schema, Schema.Null])` — the value or `null`.

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

const Name = Schema.NullOr(Schema.String) // string | null
Schema.decodeUnknownSync(Name)(null) // => null
Schema.decodeUnknownSync(Name)("Ada") // => "Ada"
```

### Schema.UndefinedOr

`Schema.Union([schema, Schema.Undefined])` — the value or `undefined`.

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

const Bio = Schema.UndefinedOr(Schema.String) // string | undefined
Schema.decodeUnknownSync(Bio)(undefined) // => undefined
```

### Schema.NullishOr

`Schema.Union([schema, Schema.Null, Schema.Undefined])` — the value, `null`, or
`undefined`.

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

const Avatar = Schema.NullishOr(Schema.String) // string | null | undefined
Schema.decodeUnknownSync(Avatar)(null) // => null
Schema.decodeUnknownSync(Avatar)(undefined) // => undefined
```

> The `OptionFrom*` combinators (decoding `null`/`undefined`/missing into an
> `Option`) are codecs, so they live in
> [transformations](https://effect.plants.sh/schema/transformations/). For *discriminated* unions keyed by
> a `_tag` field, use `Schema.TaggedUnion`, covered in
> [structs and records](https://effect.plants.sh/schema/structs-and-records/).

## Custom primitives: `declare`

When none of the built-ins fit, `Schema.declare` builds an opaque schema from a
type-guard. The schema accepts any value and succeeds when the guard returns
`true`. It's the lowest-level primitive constructor — `instanceOf` is built on top
of it.

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

type UserId = string & { readonly _tag: "UserId" }

const isUserId = (u: unknown): u is UserId =>
  typeof u === "string" && u.startsWith("user_")

const UserId = Schema.declare<UserId>(isUserId, {
  title: "UserId",
  description: "A user identifier starting with 'user_'"
})

Schema.decodeUnknownSync(UserId)("user_42") // => "user_42"
// Schema.decodeUnknownSync(UserId)("nope") -> throws
```

For **parametric** types — `Option<A>`, a custom `Box<A>`, etc. — use
`Schema.declareConstructor`, which threads the inner type parameters' schemas
through. This is an advanced escape hatch; reach for it only when a type has type
parameters that must themselves be validated.

## Where to go next

- Decode primitives from strings (`NumberFromString`, `DateFromString`, the
  `*FromString` family) and convert to `Option`/`Map`/`Set`:
  [transformations](https://effect.plants.sh/schema/transformations/).
- Tighten a primitive with custom validations: [filters](https://effect.plants.sh/schema/filters/).
- Assemble primitives into objects and collections:
  [structs and records](https://effect.plants.sh/schema/structs-and-records/).