{"id":13496110,"url":"https://github.com/Effect-TS/schema","last_synced_at":"2025-03-28T17:35:02.833Z","repository":{"id":62950737,"uuid":"561736807","full_name":"Effect-TS/schema","owner":"Effect-TS","description":"Modeling the schema of data structures as first-class values","archived":true,"fork":false,"pushed_at":"2023-12-31T15:53:12.000Z","size":7160,"stargazers_count":498,"open_issues_count":0,"forks_count":37,"subscribers_count":14,"default_branch":"main","last_synced_at":"2024-10-28T09:46:47.841Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://effect.website","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Effect-TS.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":["mikearnaldi"]}},"created_at":"2022-11-04T11:28:03.000Z","updated_at":"2024-10-14T21:04:37.000Z","dependencies_parsed_at":"2024-01-14T04:54:17.854Z","dependency_job_id":"1799411c-901d-4e8e-86af-ac5d7bfd5ecd","html_url":"https://github.com/Effect-TS/schema","commit_stats":{"total_commits":690,"total_committers":19,"mean_commits":36.31578947368421,"dds":"0.17826086956521736","last_synced_commit":"73040b0615c445bb775545f3f2035176c3f6e898"},"previous_names":["fp-ts/schema"],"tags_count":149,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Effect-TS%2Fschema","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Effect-TS%2Fschema/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Effect-TS%2Fschema/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Effect-TS%2Fschema/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Effect-TS","download_url":"https://codeload.github.com/Effect-TS/schema/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":222402977,"owners_count":16978768,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T19:01:42.423Z","updated_at":"2024-10-31T11:30:44.914Z","avatar_url":"https://github.com/Effect-TS.png","language":"TypeScript","funding_links":["https://github.com/sponsors/mikearnaldi"],"categories":["TypeScript","others"],"sub_categories":[],"readme":"# ⚠️ @effect/schema has moved\n\nThis repository has been deprecated following the consolidation of its codebase into the [`effect` monorepo](https://github.com/Effect-TS/effect).\n\nYou can find `@effect/schema` here: https://github.com/effect-ts/effect/tree/main/packages/schema\n\n# Introduction\n\nWelcome to the documentation for `@effect/schema`, **a library for defining and using schemas** to validate and transform data in TypeScript.\n\n`@effect/schema` allows you to define a `Schema\u003cI, A\u003e` that provides a blueprint for describing the structure and data types of your data. Once defined, you can leverage this schema to perform a range of operations, including:\n\n| Operation       | Description                                                                                                     |\n| --------------- | --------------------------------------------------------------------------------------------------------------- |\n| Parsing         | Convert from `unknown` value to output type `A`.                                                                |\n| Decoding        | Transforming data from an input type `I` to an output type `A`.                                                 |\n| Encoding        | Converting data from an output type `A` back to an input type `I`.                                              |\n| Asserting       | Verifying that a value adheres to the schema's output type `A`.                                                 |\n| Arbitraries     | Generate arbitraries for [fast-check](https://github.com/dubzzz/fast-check) testing.                            |\n| Pretty printing | Support pretty printing for data structures.                                                                    |\n| JSON Schemas    | Create JSON Schemas based on defined schemas.                                                                   |\n| Equivalence     | Create [Equivalences](https://effect-ts.github.io/effect/modules/Equivalence.ts.html) based on defined schemas. |\n\nIf you're eager to learn how to define your first schema, jump straight to the [**Basic usage**](#basic-usage) section!\n\n## Understanding Parsing, Decoding, and Encoding\n\nWe'll break down these concepts using an example with a `Schema\u003cstring, Date\u003e`. This schema serves as a tool to transform a `string` into a `Date` and vice versa.\n\n**Encoding**\n\nWhen we talk about \"encoding,\" we are referring to the process of changing a `Date` into a `string`. To put it simply, it's the act of converting data from one format to another.\n\n**Decoding**\n\nConversely, \"decoding\" entails transforming a `string` back into a `Date`. It's essentially the reverse operation of encoding, where data is returned to its original form.\n\n**Parsing**\n\nParsing involves two key steps:\n\n1. **Checking:** Initially, we verify that the input data (which is of the `unknown` type) matches the expected structure. In our specific case, this means ensuring that the input is indeed a `string`.\n\n2. **Decoding:** Following the successful check, we proceed to convert the `string` into a `Date`. This process completes the parsing operation, where the data is both validated and transformed.\n\n\u003e [!NOTE]\n\u003e As a general rule, schemas should be defined such that encode + decode return the original value.\n\n## The Rule of Schemas: Keeping Encode and Decode in Sync\n\nWhen working with schemas, there's an important rule to keep in mind: your schemas should be crafted in a way that when you perform both encoding and decoding operations, you should end up with the original value.\n\nIn simpler terms, if you encode a value and then immediately decode it, the result should match the original value you started with. This rule ensures that your data remains consistent and reliable throughout the encoding and decoding process.\n\n# Credits\n\nThis library was inspired by the following projects:\n\n- [io-ts](https://github.com/gcanti/io-ts)\n- [zod](https://github.com/colinhacks/zod)\n- [zio-schema](https://github.com/zio/zio-schema)\n\n# Requirements\n\n- TypeScript 5.0 or newer\n- The `strict` flag enabled in your `tsconfig.json` file\n- The `exactOptionalPropertyTypes` flag enabled in your `tsconfig.json` file\n  ```\n  {\n    // ...\n    \"compilerOptions\": {\n      // ...\n      \"strict\": true,\n      \"exactOptionalPropertyTypes\": true\n    }\n  }\n  ```\n- Additionally, make sure to install the following packages, as they are peer dependencies. Note that some package managers might not install peer dependencies by default, so you need to install them manually:\n  - `effect` package (peer dependency)\n  - [fast-check](https://github.com/dubzzz/fast-check) package (peer dependency)\n\n## Understanding `exactOptionalPropertyTypes`\n\nThe `@effect/schema` library takes advantage of the `exactOptionalPropertyTypes` option of `tsconfig.json`. This option affects how optional properties are typed (to learn more about this option, you can refer to the official [TypeScript documentation](https://www.typescriptlang.org/tsconfig#exactOptionalPropertyTypes)).\n\nLet's delve into this with an example.\n\n**With `exactOptionalPropertyTypes` Enabled**\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n/*\nconst schema: S.Schema\u003c{\n    readonly myfield?: string; // the type is strict\n}, {\n    readonly myfield?: string; // the type is strict\n}\u003e\n*/\nconst schema = S.struct({\n  myfield: S.optional(S.string.pipe(S.nonEmpty()), {\n    exact: true,\n  }),\n});\n\nS.decodeSync(schema)({ myfield: undefined }); // Error: Type 'undefined' is not assignable to type 'string'.ts(2379)\n```\n\nHere, notice that the type of `myfield` is strict (`string`), which means the type checker will catch any attempt to assign an invalid value (like `undefined`).\n\n**With `exactOptionalPropertyTypes` Disabled**\n\nIf, for some reason, you can't enable the `exactOptionalPropertyTypes` option (perhaps due to conflicts with other third-party libraries), you can still use `@effect/schema`. However, there will be a mismatch between the types and the runtime behavior:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n/*\nconst schema: S.Schema\u003c{\n    readonly myfield?: string | undefined; // the type is widened to string | undefined\n}, {\n    readonly myfield?: string | undefined; // the type is widened to string | undefined\n}\u003e\n*/\nconst schema = S.struct({\n  myfield: S.optional(S.string.pipe(S.nonEmpty()), {\n    exact: true,\n  }),\n});\n\nS.decodeSync(schema)({ myfield: undefined }); // No type error, but a decoding failure occurs\n/*\nError: error(s) found\n└─ [\"a\"]\n   └─ Expected string, actual undefined\n*/\n```\n\nIn this case, the type of `myfield` is widened to `string | undefined`, which means the type checker won't catch the invalid value (`undefined`). However, during decoding, you'll encounter an error, indicating that `undefined` is not allowed.\n\n# Getting started\n\nTo install the **alpha** version:\n\n```\nnpm install @effect/schema\n```\n\nAdditionally, make sure to install the following packages, as they are peer dependencies. Note that some package managers might not install peer dependencies by default, so you need to install them manually:\n\n- `effect` package (peer dependency)\n- [fast-check](https://github.com/dubzzz/fast-check) package (peer dependency)\n\n\u003e [!WARNING]\n\u003e This package is primarily published to receive early feedback and for contributors, during this development phase we cannot guarantee the stability of the APIs, consider each release to contain breaking changes.\n\nOnce you have installed the library, you can import the necessary types and functions from the `@effect/schema/Schema` module.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n```\n\n# Defining a schema\n\nTo define a `Schema`, you can use the provided `struct` function to define a new `Schema` that describes an object with a fixed set of properties. Each property of the object is described by a `Schema`, which specifies the data type and validation rules for that property.\n\nFor example, consider the following `Schema` that describes a person object with a `name` property of type `string` and an `age` property of type `number`:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n```\n\nYou can also use the `union` function to define a `Schema` that describes a value that can be one of a fixed set of types. For example, the following `Schema` describes a value that can be either a `string` or a `number`:\n\n```ts\nconst StringOrNumber = S.union(S.string, S.number);\n```\n\nIn addition to the provided `struct` and `union` functions, `@effect/schema/Schema` also provides a number of other functions for defining `Schema`s, including functions for defining arrays, tuples, and records.\n\n## Extracting Inferred Types\n\nAfter you've defined a `Schema\u003cI, A\u003e`, you can extract the inferred type `A` that represents the data described by the schema using the `Schema.To` type.\n\nFor instance, with the `Person` schema we defined earlier, you can extract the inferred type of a `Person` object as demonstrated below:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\ninterface Person extends S.Schema.To\u003ctypeof Person\u003e {}\n/*\nEquivalent to:\ninterface Person {\n  readonly name: string;\n  readonly age: number;\n}\n*/\n```\n\nAlternatively you can also extract a `type` instead of an `interface`:\n\n```ts\ntype Person = S.Schema.To\u003ctypeof Person\u003e;\n/*\nEquivalent to:\ntype Person {\n  readonly name: string;\n  readonly age: number;\n}\n*/\n```\n\n### Advanced extracting Inferred Types\n\nIn cases where `I` differs from `A`, you can also extract the inferred `I` type using `Schema.From`.\n\nTo create a schema with an opaque type, you can use the following technique that re-declares the schema:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst _Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\ninterface Person extends S.Schema.To\u003ctypeof _Person\u003e {}\n\n// Re-declare the schema to create a schema with an opaque type\nconst Person: S.Schema\u003cPerson\u003e = _Person;\n```\n\nAlternatively, you can use `Schema.Class` (see the [Class](#classes) section below for more details).\n\nNote that the technique shown above becomes more complex when the schema is defined such that `A` is different from `I`. For example:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n/*\nconst _Person: S.Schema\u003c{\n    readonly name: string;\n    readonly age: string;\n}, {\n    readonly name: string;\n    readonly age: number;\n}\u003e\n*/\nconst _Person = S.struct({\n  name: S.string,\n  age: S.NumberFromString,\n});\n\ninterface Person extends S.Schema.To\u003ctypeof _Person\u003e {}\n\ninterface PersonFrom extends S.Schema.From\u003ctypeof _Person\u003e {}\n\n// Re-declare the schema to create a schema with an opaque type\nconst Person: S.Schema\u003cPersonFrom, Person\u003e = _Person;\n```\n\nIn this case, the field `\"age\"` is of type `string` in the `From` type of the schema and is of type `number` in the `To` type of the schema. Therefore, we need to define **two** interfaces (`PersonFrom` and `Person`) and use both to redeclare our final schema `Person`.\n\n## Parsing\n\nTo parse a value from the `unknown` type using the previously defined `Schema`, you can make use of the `parse*` functions provided by the `@effect/schema/Schema` module. Let's explore an example using the `parseEither` function:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Either from \"effect/Either\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\nconst parsePerson = S.parseEither(Person);\n\nconst input: unknown = { name: \"Alice\", age: 30 };\n\nconst result1 = parsePerson(input);\nif (Either.isRight(result1)) {\n  console.log(result1.right);\n  /*\n  Output:\n  { name: \"Alice\", age: 30 }\n  */\n}\n\nconst result2 = parsePerson(null);\nif (Either.isLeft(result2)) {\n  console.log(result2.left);\n  /*\n  Output:\n  {\n    _id: 'ParseError',\n    message: 'error(s) found\\n└─ Expected \u003canonymous type literal schema\u003e, actual null'\n  }\n  */\n}\n```\n\nThe `parsePerson` function returns an `Either\u003cParseError, A\u003e`, where `ParseError` is defined as follows:\n\n```ts\ninterface ParseError {\n  readonly _tag: \"ParseError\";\n  // A non-empty list of errors\n  readonly errors: ReadonlyArray.NonEmptyReadonlyArray\u003cParseIssue\u003e;\n}\n```\n\n`ParseError` represents a list of errors that may have occurred during the parsing process, and `A` is the inferred data type described by the `Schema`. A successful parse results in a `Right` value, containing the parsed data `A`. In the case of a failed parse, the result will be a `Left` value containing a non-empty list of `ParseIssue`.\n\nNow, let's see another example using the `parseSync` function.\n\nThe `parseSync` function is used to parse a value and throw an error if the parsing fails. This is especially useful when you want to ensure that the parsed value adheres to the correct format and are ready to throw an error if it does not.\n\n```ts\ntry {\n  const person = S.parseSync(Person)({});\n  console.log(person);\n} catch (e) {\n  console.error(\"Parsing failed:\");\n  console.error(e);\n}\n/*\nParsing failed:\nError: error(s) found\n└─ [\"name\"]\n   └─ is missing\n  ...stack...\n*/\n```\n\nIn this example, we attempt to parse an empty object, but the `name` property is missing, resulting in an error being thrown.\n\n### Handling Async Transformations\n\nWhen your schema involves asynchronous transformations, neither the `parseSync` nor the `parseEither` functions will work for you. In such cases, you must turn to the `parse` function, which returns an `Effect`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport { Effect } from \"effect\";\n\nconst PersonId = S.number;\n\nconst Person = S.struct({\n  id: PersonId,\n  name: S.string,\n  age: S.number,\n});\n\nconst asyncSchema = S.transformOrFail(\n  PersonId,\n  Person,\n  // Simulate an async transformation\n  (id) =\u003e\n    Effect.succeed({ id, name: \"name\", age: 18 }).pipe(\n      Effect.delay(\"10 millis\")\n    ),\n  (person) =\u003e Effect.succeed(person.id).pipe(Effect.delay(\"10 millis\"))\n);\n\nconst syncParsePersonId = S.parseEither(asyncSchema);\n\nconsole.log(JSON.stringify(syncParsePersonId(1), null, 2));\n/*\nOutput:\n{\n  \"_id\": \"Either\",\n  \"_tag\": \"Left\",\n  \"left\": {\n    \"_id\": \"ParseError\",\n    \"message\": \"error(s) found\\n└─ is forbidden\"\n  }\n}\n*/\n\nconst asyncParsePersonId = S.parse(asyncSchema);\n\nEffect.runPromise(asyncParsePersonId(1)).then(console.log);\n/*\nOutput:\n{ id: 1, name: 'name', age: 18 }\n*/\n```\n\nAs shown in the code above, the first approach returns a `Forbidden` error, indicating that using `parseEither` with an async transformation is not allowed. However, the second approach works as expected, allowing you to handle async transformations and return the desired result.\n\n### Excess properties\n\nWhen using a `Schema` to parse a value, any properties that are not specified in the `Schema` will be stripped out from the output. This is because the `Schema` is expecting a specific shape for the parsed value, and any excess properties do not conform to that shape.\n\nHowever, you can use the `onExcessProperty` option (default value: `\"ignore\"`) to trigger a parsing error. This can be particularly useful in cases where you need to detect and handle potential errors or unexpected values.\n\nHere's an example of how you might use `onExcessProperty`:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\nconsole.log(\n  S.parseSync(Person)({\n    name: \"Bob\",\n    age: 40,\n    email: \"bob@example.com\",\n  })\n);\n/*\nOutput:\n{ name: 'Bob', age: 40 }\n*/\n\nS.parseSync(Person)(\n  {\n    name: \"Bob\",\n    age: 40,\n    email: \"bob@example.com\",\n  },\n  { onExcessProperty: \"error\" }\n);\n/*\nthrows\nError: error(s) found\n└─ [\"email\"]\n   └─ is unexpected, expected \"name\" or \"age\"\n*/\n```\n\n### All errors\n\nThe `errors` option allows you to receive all parsing errors when attempting to parse a value using a schema. By default only the first error is returned, but by setting the `errors` option to `\"all\"`, you can receive all errors that occurred during the parsing process. This can be useful for debugging or for providing more comprehensive error messages to the user.\n\nHere's an example of how you might use `errors`:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\nS.parseSync(Person)(\n  {\n    name: \"Bob\",\n    age: \"abc\",\n    email: \"bob@example.com\",\n  },\n  { errors: \"all\", onExcessProperty: \"error\" }\n);\n/*\nthrows\nError: error(s) found\n├─ [\"email\"]\n│  └─ is unexpected, expected \"name\" or \"age\"\n└─ [\"age\"]\n   └─ Expected number, actual \"abc\"\n*/\n```\n\n## Encoding\n\nTo use the `Schema` defined above to encode a value to `unknown`, you can use the `encode` function:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Either from \"effect/Either\";\n\n// Age is a schema that can decode a string to a number and encode a number to a string\nconst Age = S.NumberFromString;\n\nconst Person = S.struct({\n  name: S.string,\n  age: Age,\n});\n\nconst encoded = S.encodeEither(Person)({ name: \"Alice\", age: 30 });\nif (Either.isRight(encoded)) {\n  console.log(encoded.right);\n  /*\n  Output:\n  { name: \"Alice\", age: \"30\" }\n  */\n}\n```\n\nNote that during encoding, the number value `30` was converted to a string `\"30\"`.\n\n## Formatting Errors\n\nWhen you're working with Effect Schema and encounter errors during parsing, decoding, or encoding functions, you can format these errors in two different ways: using the `TreeFormatter` or the `ArrayFormatter`.\n\n### TreeFormatter (default)\n\nThe `TreeFormatter` is the default way to format errors. It arranges errors in a tree structure, making it easy to see the hierarchy of issues.\n\nHere's an example of how it works:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport { formatErrors } from \"@effect/schema/TreeFormatter\";\nimport * as Either from \"effect/Either\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\nconst result = S.parseEither(Person)({});\nif (Either.isLeft(result)) {\n  console.error(\"Parsing failed:\");\n  console.error(formatErrors(result.left.errors));\n}\n/*\nParsing failed:\nerror(s) found\n└─ [\"name\"]\n   └─ is missing\n*/\n```\n\n### ArrayFormatter\n\nThe `ArrayFormatter` is an alternative way to format errors, presenting them as an array of issues. Each issue contains properties such as `_tag`, `path`, and `message`:\n\n```ts\ninterface Issue {\n  readonly _tag: ParseErrors[\"_tag\"];\n  readonly path: ReadonlyArray\u003cPropertyKey\u003e;\n  readonly message: string;\n}\n```\n\nHere's an example of how it works:\n\n```ts\nimport { formatErrors } from \"@effect/schema/ArrayFormatter\";\nimport * as S from \"@effect/schema/Schema\";\nimport * as Either from \"effect/Either\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\nconst result = S.parseEither(Person)(\n  { name: 1, foo: 2 },\n  { errors: \"all\", onExcessProperty: \"error\" }\n);\nif (Either.isLeft(result)) {\n  console.error(\"Parsing failed:\");\n  console.error(formatErrors(result.left.errors));\n}\n/*\nParsing failed:\n[\n  {\n    _tag: 'Unexpected',\n    path: [ 'foo' ],\n    message: 'Unexpected, expected \"name\" or \"age\"'\n  },\n  {\n    _tag: 'Type',\n    path: [ 'name' ],\n    message: 'Expected string, actual 1'\n  },\n  { _tag: 'Missing', path: [ 'age' ], message: 'Missing key or index' }\n]\n*/\n```\n\n## Assertions\n\nThe `is` function provided by the `@effect/schema/Schema` module represents a way of verifying that a value conforms to a given `Schema`. `is` is a refinement that takes a value of type `unknown` as an argument and returns a `boolean` indicating whether or not the value conforms to the `Schema`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\n// const isPerson: (u: unknown) =\u003e u is Person\nconst isPerson = S.is(Person);\n\nconsole.log(isPerson({ name: \"Alice\", age: 30 })); // true\nconsole.log(isPerson(null)); // false\nconsole.log(isPerson({})); // false\n```\n\nThe `asserts` function takes a `Schema` and returns a function that takes an input value and checks if it matches the schema. If it does not match the schema, it throws an error with a comprehensive error message.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\n// const assertsPerson: (input: unknown, options?: ParseOptions) =\u003e asserts input is { readonly name: string; readonly age: number; }\nconst assertsPerson: S.Schema.ToAsserts\u003ctypeof Person\u003e = S.asserts(Person);\n\ntry {\n  assertsPerson({ name: \"Alice\", age: \"30\" });\n} catch (e) {\n  console.error(\"The input does not match the schema:\");\n  console.error(e);\n}\n/*\nThe input does not match the schema:\nError: error(s) found\n└─ [\"age\"]\n   └─ Expected number, actual \"30\"\n*/\n\n// this will not throw an error\nassertsPerson({ name: \"Alice\", age: 30 });\n```\n\n## [fast-check](https://github.com/dubzzz/fast-check) arbitraries\n\nThe `arbitrary` function provided by the `@effect/schema/Arbitrary` module represents a way of generating random values that conform to a given `Schema`. This can be useful for testing purposes, as it allows you to generate random test data that is guaranteed to be valid according to the `Schema`.\n\n```ts\nimport * as Arbitrary from \"@effect/schema/Arbitrary\";\nimport * as S from \"@effect/schema/Schema\";\nimport * as fc from \"fast-check\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.string.pipe(S.numberFromString, S.int()),\n});\n\n// Arbitrary for the To type\nconst PersonArbitraryTo = Arbitrary.to(Person)(fc);\n\nconsole.log(fc.sample(PersonArbitraryTo, 2));\n/*\nOutput:\n[ { name: 'iP=!', age: -6 }, { name: '', age: 14 } ]\n*/\n\n// Arbitrary for the From type\nconst PersonArbitraryFrom = Arbitrary.from(Person)(fc);\n\nconsole.log(fc.sample(PersonArbitraryFrom, 2));\n/*\nOutput:\n[ { name: '{F', age: '$\"{|' }, { name: 'nB}@BK', age: '^V+|W!Z' } ]\n*/\n```\n\n### Troubleshooting: Dealing with `\"type\": \"module\"` in `package.json`\n\nIf you have set the `\"type\"` field in your `package.json` to `\"module\"`, you might encounter the following error:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Arbitrary from \"@effect/schema/Arbitrary\";\nimport * as fc from \"fast-check\";\n\nconst arb = Arbitrary.to(S.string)(fc);\n/*\n...more lines...\n  Types have separate declarations of a private property 'internalRng'.\n*/\n```\n\nTo address this issue, you can apply a patch, for example using `pnpm patch`, to the `fast-check` package in the `node_modules` directory:\n\n```diff\ndiff --git a/CHANGELOG.md b/CHANGELOG.md\ndeleted file mode 100644\nindex 41d6274a9d4bb2d9924fb82f77e502f232fd12f5..0000000000000000000000000000000000000000\ndiff --git a/package.json b/package.json\nindex e871dfde5f8877b1b7de9bd3d9a6e3e4f7f59843..819035d70e22d246c615bda25183db9b5e124287 100644\n--- a/package.json\n+++ b/package.json\n@@ -12,7 +12,7 @@\n         \"default\": \"./lib/fast-check.js\"\n       },\n       \"import\": {\n-        \"types\": \"./lib/esm/types/fast-check.d.ts\",\n+        \"types\": \"./lib/types/fast-check.d.ts\",\n         \"default\": \"./lib/esm/fast-check.js\"\n       }\n     }\n```\n\nThis patch helps resolve the issue caused by the declaration of a private property 'internalRng' having separate declarations in the types when using `\"type\": \"module\"` in `package.json`.\n\n## Pretty print\n\nThe `to` function provided by the `@effect/schema/Pretty` module represents a way of pretty-printing values that conform to a given `Schema`.\n\nYou can use the `to` function to create a human-readable string representation of a value that conforms to a `Schema`. This can be useful for debugging or logging purposes, as it allows you to easily inspect the structure and data types of the value.\n\n```ts\nimport * as Pretty from \"@effect/schema/Pretty\";\nimport * as S from \"@effect/schema/Schema\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\nconst PersonPretty = Pretty.to(Person);\n\n// returns a string representation of the object\nconsole.log(PersonPretty({ name: \"Alice\", age: 30 }));\n/*\nOutput:\n{ \"name\": \"Alice\", \"age\": 30 }\n*/\n```\n\n## Generating JSON Schemas\n\nThe `to` / `from` functions, which are part of the `@effect/schema/JSONSchema` module, allow you to generate a JSON Schema based on a schema definition:\n\n```ts\nimport * as JSONSchema from \"@effect/schema/JSONSchema\";\nimport * as S from \"@effect/schema/Schema\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\nconst jsonSchema = JSONSchema.to(Person);\n\nconsole.log(JSON.stringify(jsonSchema, null, 2));\n/*\nOutput:\n{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\n    \"name\",\n    \"age\"\n  ],\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\",\n      \"description\": \"a string\",\n      \"title\": \"string\"\n    },\n    \"age\": {\n      \"type\": \"number\",\n      \"description\": \"a number\",\n      \"title\": \"number\"\n    }\n  },\n  \"additionalProperties\": false\n}\n*/\n```\n\nIn this example, we have created a schema for a \"Person\" with a name (a string) and an age (a number). We then use the `JSONSchema.to` function to generate the corresponding JSON Schema.\n\n### Identifier Annotations\n\nYou can enhance your schemas with identifier annotations. If you do, your schema will be included within a \"definitions\" object property on the root and referenced from there:\n\n```ts\nimport * as JSONSchema from \"@effect/schema/JSONSchema\";\nimport * as S from \"@effect/schema/Schema\";\n\nconst Name = S.string.pipe(S.identifier(\"Name\"));\nconst Age = S.number.pipe(S.identifier(\"Age\"));\nconst Person = S.struct({\n  name: Name,\n  age: Age,\n});\n\nconst jsonSchema = JSONSchema.to(Person);\n\nconsole.log(JSON.stringify(jsonSchema, null, 2));\n/*\nOutput:\n{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"type\": \"object\",\n  \"required\": [\n    \"name\",\n    \"age\"\n  ],\n  \"properties\": {\n    \"name\": {\n      \"$ref\": \"#/$defs/Name\"\n    },\n    \"age\": {\n      \"$ref\": \"#/$defs/Age\"\n    }\n  },\n  \"additionalProperties\": false,\n  \"$defs\": {\n    \"Name\": {\n      \"type\": \"string\",\n      \"description\": \"a string\",\n      \"title\": \"string\"\n    },\n    \"Age\": {\n      \"type\": \"number\",\n      \"description\": \"a number\",\n      \"title\": \"number\"\n    }\n  }\n}\n*/\n```\n\nThis technique helps organize your JSON Schema by creating separate definitions for each identifier annotated schema, making it more readable and maintainable.\n\n### Recursive and Mutually Recursive Schemas\n\nRecursive and mutually recursive schemas are supported, but in these cases, identifier annotations are **required**:\n\n```ts\nimport * as JSONSchema from \"@effect/schema/JSONSchema\";\nimport * as S from \"@effect/schema/Schema\";\n\ninterface Category {\n  readonly name: string;\n  readonly categories: ReadonlyArray\u003cCategory\u003e;\n}\n\nconst schema: S.Schema\u003cCategory\u003e = S.struct({\n  name: S.string,\n  categories: S.array(S.suspend(() =\u003e schema).pipe(S.identifier(\"Category\"))),\n});\n\nconst jsonSchema = JSONSchema.to(schema);\n\nconsole.log(JSON.stringify(jsonSchema, null, 2));\n/*\nOutput:\n{\n  \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n  \"$ref\": \"#/$defs/Category\",\n  \"$defs\": {\n    \"Category\": {\n      \"type\": \"object\",\n      \"required\": [\n        \"name\",\n        \"categories\"\n      ],\n      \"properties\": {\n        \"name\": {\n          \"type\": \"string\",\n          \"description\": \"a string\",\n          \"title\": \"string\"\n        },\n        \"categories\": {\n          \"type\": \"array\",\n          \"items\": {\n            \"$ref\": \"#/$defs/Category\"\n          }\n        }\n      },\n      \"additionalProperties\": false\n    }\n  }\n}\n*/\n```\n\nIn the example above, we define a schema for a \"Category\" that can contain a \"name\" (a string) and an array of nested \"categories.\" To support recursive definitions, we use the `S.suspend` function and identifier annotations to name our schema.\n\nThis ensures that the JSON Schema properly handles the recursive structure and creates distinct definitions for each annotated schema, improving readability and maintainability.\n\n### JSON Schema Annotations\n\nWhen defining a **refinement** (e.g., through the `filter` function), you can attach a JSON Schema annotation to your schema containing a JSON Schema \"fragment\" related to this particular refinement. This fragment will be used to generate the corresponding JSON Schema. Note that if the schema consists of more than one refinement, the corresponding annotations will be merged.\n\n```ts\nimport * as JSONSchema from \"@effect/schema/JSONSchema\";\nimport * as S from \"@effect/schema/Schema\";\n\n// Simulate one or more refinements\nconst Positive = S.number.pipe(\n  S.filter((n) =\u003e n \u003e 0, {\n    jsonSchema: { minimum: 0 },\n  })\n);\n\nconst schema = Positive.pipe(\n  S.filter((n) =\u003e n \u003c= 10, {\n    jsonSchema: { maximum: 10 },\n  })\n);\n\nconsole.log(JSONSchema.to(schema));\n/*\nOutput:\n{\n  '$schema': 'http://json-schema.org/draft-07/schema#',\n  type: 'number',\n  description: 'a number',\n  title: 'number',\n  minimum: 0,\n  maximum: 10\n}\n*/\n```\n\nAs seen in the example, the JSON Schema annotations are merged with the base JSON Schema from `S.number`. This approach helps handle multiple refinements while maintaining clarity in your code.\n\n## Generating Equivalences\n\nThe `to` function, which is part of the `@effect/schema/Equivalence` module, allows you to generate an [Equivalence](https://effect-ts.github.io/effect/modules/Equivalence.ts.html) based on a schema definition:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Equivalence from \"@effect/schema/Equivalence\";\n\nconst Person = S.struct({\n  name: S.string,\n  age: S.number,\n});\n\n// $ExpectType Equivalence\u003c{ readonly name: string; readonly age: number; }\u003e\nconst PersonEquivalence = Equivalence.to(Person);\n\nconst john = { name: \"John\", age: 23 };\nconst alice = { name: \"Alice\", age: 30 };\n\nconsole.log(PersonEquivalence(john, { name: \"John\", age: 23 })); // Output: true\nconsole.log(PersonEquivalence(john, alice)); // Output: false\n```\n\n# Basic usage\n\n## Cheatsheet\n\n| Typescript Type                              | Description / Notes                      | Schema / Combinator                                       |\n| -------------------------------------------- | ---------------------------------------- | --------------------------------------------------------- |\n| `null`                                       |                                          | `S.null`                                                  |\n| `undefined`                                  |                                          | `S.undefined`                                             |\n| `string`                                     |                                          | `S.string`                                                |\n| `number`                                     |                                          | `S.number`                                                |\n| `boolean`                                    |                                          | `S.boolean`                                               |\n| `symbol`                                     |                                          | `S.symbolFromSelf` / `S.symbol`                           |\n| `bigint`                                     |                                          | `S.bigintFromSelf` / `S.bigint`                           |\n| `unknown`                                    |                                          | `S.unknown`                                               |\n| `any`                                        |                                          | `S.any`                                                   |\n| `never`                                      |                                          | `S.never`                                                 |\n| `object`                                     |                                          | `S.object`                                                |\n| `unique symbol`                              |                                          | `S.uniqueSymbol`                                          |\n| `\"a\"`, `1`, `true`                           | type literals                            | `S.literal(\"a\")`, `S.literal(1)`, `S.literal(true)`       |\n| `a${string}`                                 | template literals                        | `S.templateLiteral(S.literal(\"a\"), S.string)`             |\n| `{ readonly a: string, readonly b: number }` | structs                                  | `S.struct({ a: S.string, b: S.number })`                  |\n| `{ readonly a?: string }`                    | optional fields                          | `S.struct({ a: S.optional(S.string, { exact: true }) })`  |\n| `Record\u003cA, B\u003e`                               | records                                  | `S.record(A, B)`                                          |\n| `readonly [string, number]`                  | tuples                                   | `S.tuple(S.string, S.number)`                             |\n| `ReadonlyArray\u003cstring\u003e`                      | arrays                                   | `S.array(S.string)`                                       |\n| `A \\| B`                                     | unions                                   | `S.union(A, B)`                                           |\n| `A \u0026 B`                                      | intersections of non-overlapping structs | `S.extend(A, B)`                                          |\n| `Record\u003cA, B\u003e \u0026 Record\u003cC, D\u003e`                | intersections of non-overlapping records | `S.extend(S.record(A, B), S.record(C, D))`                |\n| `type A = { readonly a: A \\| null }`         | recursive types                          | `S.struct({ a: S.union(S.null, S.suspend(() =\u003e self)) })` |\n| `keyof A`                                    |                                          | `S.keyof(A)`                                              |\n| `Partial\u003cA\u003e`                                 |                                          | `S.partial(A)`                                            |\n| `Required\u003cA\u003e`                                |                                          | `S.required(A)`                                           |\n\n## Primitives\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// primitive values\nS.string;\nS.number;\nS.bigint; // Schema\u003cstring, bigint\u003e\nS.boolean;\nS.symbol; // Schema\u003cstring, symbol\u003e\nS.object;\n\n// empty types\nS.undefined;\nS.void; // accepts undefined\n\n// catch-all types\n// allows any value\nS.any;\nS.unknown;\n\n// never type\n// allows no values\nS.never;\n\nS.json;\nS.UUID;\nS.ULID;\n```\n\n## Literals\n\n```ts\nS.null; // same as S.literal(null)\nS.literal(\"a\");\nS.literal(\"a\", \"b\", \"c\"); // union of literals\nS.literal(1);\nS.literal(2n); // bigint literal\nS.literal(true);\n```\n\n## Template literals\n\nThe `templateLiteral` combinator allows you to create a schema for a TypeScript template literal type.\n\n```ts\n// $ExpectType Schema\u003c`a${string}`\u003e\nS.templateLiteral(S.literal(\"a\"), S.string);\n\n// example from https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html\nconst EmailLocaleIDs = S.literal(\"welcome_email\", \"email_heading\");\nconst FooterLocaleIDs = S.literal(\"footer_title\", \"footer_sendoff\");\n\n// $ExpectType Schema\u003c\"welcome_email_id\" | \"email_heading_id\" | \"footer_title_id\" | \"footer_sendoff_id\"\u003e\nS.templateLiteral(S.union(EmailLocaleIDs, FooterLocaleIDs), S.literal(\"_id\"));\n```\n\n## Filters\n\nIn the `@effect/schema/Schema` library, you can apply custom validation logic using _filters_.\n\nYou can define a custom validation check on any schema using the `filter` function. Here's a simple example:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst LongString = S.string.pipe(\n  S.filter((s) =\u003e s.length \u003e= 10, {\n    message: () =\u003e \"a string at least 10 characters long\",\n  })\n);\n\nconsole.log(S.parseSync(LongString)(\"a\"));\n/*\nthrows:\nerror(s) found\n└─ a string at least 10 characters long\n*/\n```\n\nIt's recommended to include as much metadata as possible for later introspection of the schema, such as an identifier, JSON schema representation, and a description:\n\n```ts\nconst LongString = S.string.pipe(\n  S.filter((s) =\u003e s.length \u003e= 10, {\n    message: () =\u003e \"a string at least 10 characters long\",\n    identifier: \"LongString\",\n    jsonSchema: { minLength: 10 },\n    description:\n      \"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua\",\n  })\n);\n```\n\nFor more complex scenarios, you can return an `Option\u003cParseError\u003e` type instead of a boolean. In this context, `None` indicates success, and `Some(error)` rejects the input with a specific error. Here's an example:\n\n```ts\nimport * as ParseResult from \"@effect/schema/ParseResult\";\nimport * as S from \"@effect/schema/Schema\";\n\nconst schema = S.struct({ a: S.string, b: S.string }).pipe(\n  S.filter((o) =\u003e\n    o.b === o.a\n      ? Option.none()\n      : Option.some(\n          ParseResult.parseError([\n            ParseResult.key(\"b\", [\n              ParseResult.type(\n                S.literal(o.a).ast,\n                o.b,\n                `should be equal to a's value (\"${o.a}\")`\n              ),\n            ]),\n          ])\n        )\n  )\n);\n\nconsole.log(S.parseSync(schema)({ a: \"a\", b: \"b\" }));\n/*\nthrows:\nerror(s) found\n└─ [\"b\"]\n   └─ should be equal to a's value (\"a\")\n*/\n```\n\n\u003e [!WARNING]\n\u003e Please note that the use of filters do not alter the type of the `Schema`. They only serve to add additional constraints to the parsing process.\n\n### String filters\n\n```ts\nS.string.pipe(S.maxLength(5));\nS.string.pipe(S.minLength(5));\nS.string.pipe(nonEmpty()); // same as S.minLength(1)\nS.string.pipe(S.length(5));\nS.string.pipe(S.pattern(regex));\nS.string.pipe(S.startsWith(string));\nS.string.pipe(S.endsWith(string));\nS.string.pipe(S.includes(searchString));\nS.string.pipe(S.trimmed()); // verifies that a string contains no leading or trailing whitespaces\nS.string.pipe(S.lowercased()); // verifies that a string is lowercased\n```\n\n**Note**: The `trimmed` combinator does not make any transformations, it only validates. If what you were looking for was a combinator to trim strings, then check out the `trim` combinator ot the `Trim` schema.\n\n### Number filters\n\n```ts\nS.number.pipe(S.greaterThan(5));\nS.number.pipe(S.greaterThanOrEqualTo(5));\nS.number.pipe(S.lessThan(5));\nS.number.pipe(S.lessThanOrEqualTo(5));\nS.number.pipe(S.between(-2, 2)); // -2 \u003c= x \u003c= 2\n\nS.number.pipe(S.int()); // value must be an integer\n\nS.number.pipe(S.nonNaN()); // not NaN\nS.number.pipe(S.finite()); // ensures that the value being parsed is finite and not equal to Infinity or -Infinity\n\nS.number.pipe(S.positive()); // \u003e 0\nS.number.pipe(S.nonNegative()); // \u003e= 0\nS.number.pipe(S.negative()); // \u003c 0\nS.number.pipe(S.nonPositive()); // \u003c= 0\n\nS.number.pipe(S.multipleOf(5)); // evenly divisible by 5\n```\n\n### Bigint filters\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nS.bigint.pipe(S.greaterThanBigint(5n));\nS.bigint.pipe(S.greaterThanOrEqualToBigint(5n));\nS.bigint.pipe(S.lessThanBigint(5n));\nS.bigint.pipe(S.lessThanOrEqualToBigint(5n));\nS.bigint.pipe(S.betweenBigint(-2n, 2n)); // -2n \u003c= x \u003c= 2n\n\nS.bigint.pipe(S.positiveBigint()); // \u003e 0n\nS.bigint.pipe(S.nonNegativeBigint()); // \u003e= 0n\nS.bigint.pipe(S.negativeBigint()); // \u003c 0n\nS.bigint.pipe(S.nonPositiveBigint()); // \u003c= 0n\n```\n\n### BigDecimal filters\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as BigDecimal from \"effect/BigDecimal\";\n\nS.BigDecimal.pipe(S.greaterThanBigDecimal(BigDecimal.fromNumber(5)));\nS.BigDecimal.pipe(S.greaterThanOrEqualToBigDecimal(BigDecimal.fromNumber(5)));\nS.BigDecimal.pipe(S.lessThanBigDecimal(BigDecimal.fromNumber(5)));\nS.BigDecimal.pipe(S.lessThanOrEqualToBigDecimal(BigDecimal.fromNumber(5)));\nS.BigDecimal.pipe(\n  S.betweenBigDecimal(BigDecimal.fromNumber(-2), BigDecimal.fromNumber(2))\n);\n\nS.BigDecimal.pipe(S.positiveBigDecimal());\nS.BigDecimal.pipe(S.nonNegativeBigDecimal());\nS.BigDecimal.pipe(S.negativeBigDecimal());\nS.BigDecimal.pipe(S.nonPositiveBigDecimal());\n```\n\n### Duration filters\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Duration from \"effect/BigDecimal\";\n\nS.Duration.pipe(S.greaterThanDuration(\"5 seconds\"));\nS.Duration.pipe(S.greaterThanOrEqualToDuration(\"5 seconds\"));\nS.Duration.pipe(S.lessThanDuration(\"5 seconds\"));\nS.Duration.pipe(S.lessThanOrEqualToDuration(\"5 seconds\"));\nS.Duration.pipe(S.betweenDuration(\"5 seconds\", \"10 seconds\"));\n```\n\n### Array filters\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nS.array(S.number).pipe(S.maxItems(2)); // max array length\nS.array(S.number).pipe(S.minItems(2)); // min array length\nS.array(S.number).pipe(S.itemsCount(2)); // exact array length\n```\n\n## Branded types\n\nTypeScript's type system is structural, which means that any two types that are structurally equivalent are considered the same. This can cause issues when types that are semantically different are treated as if they were the same.\n\n```ts\ntype UserId = string\ntype Username = string\n\nconst getUser = (id: UserId) =\u003e { ... }\n\nconst myUsername: Username = \"gcanti\"\n\ngetUser(myUsername) // works fine\n```\n\nIn the above example, `UserId` and `Username` are both aliases for the same type, `string`. This means that the `getUser` function can mistakenly accept a `Username` as a valid `UserId`, causing bugs and errors.\n\nTo avoid these kinds of issues, the `@effect` ecosystem provides a way to create custom types with a unique identifier attached to them. These are known as \"branded types\".\n\n```ts\nimport type * as B from \"effect/Brand\"\n\ntype UserId = string \u0026 B.Brand\u003c\"UserId\"\u003e\ntype Username = string\n\nconst getUser = (id: UserId) =\u003e { ... }\n\nconst myUsername: Username = \"gcanti\"\n\ngetUser(myUsername) // error\n```\n\nBy defining `UserId` as a branded type, the `getUser` function can accept only values of type `UserId`, and not plain strings or other types that are compatible with strings. This helps to prevent bugs caused by accidentally passing the wrong type of value to the function.\n\nThere are two ways to define a schema for a branded type, depending on whether you:\n\n- want to define the schema from scratch\n- have already defined a branded type via `effect/Brand` and want to reuse it to define a schema\n\n### Defining a schema from scratch\n\nTo define a schema for a branded type from scratch, you can use the `brand` combinator exported by the `@effect/schema/Schema` module. Here's an example:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst UserId = S.string.pipe(S.brand(\"UserId\"));\ntype UserId = S.Schema.To\u003ctypeof UserId\u003e; // string \u0026 Brand\u003c\"UserId\"\u003e\n```\n\nNote that you can use `unique symbol`s as brands to ensure uniqueness across modules / packages:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst UserIdBrand = Symbol.for(\"UserId\");\nconst UserId = S.string.pipe(S.brand(UserIdBrand));\ntype UserId = S.Schema.To\u003ctypeof UserId\u003e; // string \u0026 Brand\u003ctypeof UserIdBrand\u003e\n```\n\n### Reusing an existing branded type\n\nIf you have already defined a branded type using the `effect/Brand` module, you can reuse it to define a schema using the `fromBrand` combinator exported by the `@effect/schema/Schema` module. Here's an example:\n\n```ts\nimport * as B from \"effect/Brand\";\n\n// the existing branded type\ntype UserId = string \u0026 B.Brand\u003c\"UserId\"\u003e;\nconst UserId = B.nominal\u003cUserId\u003e();\n\nimport * as S from \"@effect/schema/Schema\";\n\n// Define a schema for the branded type\nconst UserIdSchema = S.string.pipe(S.fromBrand(UserId));\n```\n\n## Native enums\n\n```ts\nenum Fruits {\n  Apple,\n  Banana,\n}\n\n// $ExpectType Schema\u003cFruits\u003e\nS.enums(Fruits);\n```\n\n## Nullables\n\n```ts\n// $ExpectType Schema\u003cstring | null\u003e\nS.nullable(S.string);\n```\n\n## Unions\n\n`@effect/schema/Schema` includes a built-in `union` combinator for composing \"OR\" types.\n\n```ts\n// $ExpectType Schema\u003cstring | number\u003e\nS.union(S.string, S.number);\n```\n\n### Union of literals\n\nWhile the following is perfectly acceptable:\n\n```ts\n// $ExpectType Schema\u003c\"a\" | \"b\" | \"c\"\u003e\nconst schema = S.union(S.literal(\"a\"), S.literal(\"b\"), S.literal(\"c\"));\n```\n\nIt is possible to use `literal` and pass multiple literals, which is less cumbersome:\n\n```ts\n// $ExpectType Schema\u003c\"a\" | \"b\" | \"c\"\u003e\nconst schema = S.literal(\"a\", \"b\", \"c\");\n```\n\nUnder the hood, they are the same, as `literal(...literals)` will be converted into a union.\n\n### Discriminated unions\n\nTypeScript reference: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#discriminated-unions\n\nDiscriminated unions in TypeScript are a way of modeling complex data structures that may take on different forms based on a specific set of conditions or properties. They allow you to define a type that represents multiple related shapes, where each shape is uniquely identified by a shared discriminant property.\n\nIn a discriminated union, each variant of the union has a common property, called the discriminant. The discriminant is a literal type, which means it can only have a finite set of possible values. Based on the value of the discriminant property, TypeScript can infer which variant of the union is currently in use.\n\nHere is an example of a discriminated union in TypeScript:\n\n```ts\ntype Circle = {\n  readonly kind: \"circle\";\n  readonly radius: number;\n};\n\ntype Square = {\n  readonly kind: \"square\";\n  readonly sideLength: number;\n};\n\ntype Shape = Circle | Square;\n```\n\nThis code defines a discriminated union using the `@effect/schema` library:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Circle = S.struct({\n  kind: S.literal(\"circle\"),\n  radius: S.number,\n});\n\nconst Square = S.struct({\n  kind: S.literal(\"square\"),\n  sideLength: S.number,\n});\n\nconst Shape = S.union(Circle, Square);\n```\n\nThe `literal` combinator is used to define the discriminant property with a specific string literal value.\n\nTwo structs are defined for `Circle` and `Square`, each with their own properties. These structs represent the variants of the union.\n\nFinally, the `union` combinator is used to create a schema for the discriminated union `Shape`, which is a union of `Circle` and `Square`.\n\n### How to transform a simple union into a discriminated union\n\nIf you're working on a TypeScript project and you've defined a simple union to represent a particular input, you may find yourself in a situation where you're not entirely happy with how it's set up. For example, let's say you've defined a `Shape` union as a combination of `Circle` and `Square` without any special property:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Circle = S.struct({\n  radius: S.number,\n});\n\nconst Square = S.struct({\n  sideLength: S.number,\n});\n\nconst Shape = S.union(Circle, Square);\n```\n\nTo make your code more manageable, you may want to transform the simple union into a discriminated union. This way, TypeScript will be able to automatically determine which member of the union you're working with based on the value of a specific property.\n\nTo achieve this, you can add a special property to each member of the union, which will allow TypeScript to know which type it's dealing with at runtime. Here's how you can transform the `Shape` schema into another schema that represents a discriminated union:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Circle = S.struct({\n  radius: S.number,\n});\n\nconst Square = S.struct({\n  sideLength: S.number,\n});\n\nconst DiscriminatedShape = S.union(\n  Circle.pipe(\n    S.transform(\n      Circle.pipe(S.extend(S.struct({ kind: S.literal(\"circle\") }))), // Add a \"kind\" property with the literal value \"circle\" to Circle\n      (circle) =\u003e ({ ...circle, kind: \"circle\" as const }), // Add the discriminant property to Circle\n      ({ kind: _kind, ...rest }) =\u003e rest // Remove the discriminant property\n    )\n  ),\n  Square.pipe(\n    S.transform(\n      Square.pipe(S.extend(S.struct({ kind: S.literal(\"square\") }))), // Add a \"kind\" property with the literal value \"square\" to Square\n      (square) =\u003e ({ ...square, kind: \"square\" as const }), // Add the discriminant property to Square\n      ({ kind: _kind, ...rest }) =\u003e rest // Remove the discriminant property\n    )\n  )\n);\n\nexpect(S.parseSync(DiscriminatedShape)({ radius: 10 })).toEqual({\n  kind: \"circle\",\n  radius: 10,\n});\n\nexpect(S.parseSync(DiscriminatedShape)({ sideLength: 10 })).toEqual({\n  kind: \"square\",\n  sideLength: 10,\n});\n```\n\nIn this example, we use the `extend` function to add a \"kind\" property with a literal value to each member of the union. Then we use `transform` to add the discriminant property and remove it afterwards. Finally, we use `union` to combine the transformed schemas into a discriminated union.\n\nHowever, when we use the schema to encode a value, we want the output to match the original input shape. Therefore, we must remove the discriminant property we added earlier from the encoded value to match the original shape of the input.\n\nThe previous solution works perfectly and shows how we can add and remove properties to our schema at will, making it easier to consume the result within our domain model. However, it requires a lot of boilerplate. Fortunately, there is an API called `attachPropertySignature` designed specifically for this use case, which allows us to achieve the same result with much less effort:\n\n```ts\nconst Circle = S.struct({ radius: S.number });\nconst Square = S.struct({ sideLength: S.number });\nconst DiscriminatedShape = S.union(\n  Circle.pipe(S.attachPropertySignature(\"kind\", \"circle\")),\n  Square.pipe(S.attachPropertySignature(\"kind\", \"square\"))\n);\n\n// parsing\nexpect(S.parseSync(DiscriminatedShape)({ radius: 10 })).toEqual({\n  kind: \"circle\",\n  radius: 10,\n});\n\n// encoding\nexpect(\n  S.encodeSync(DiscriminatedShape)({\n    kind: \"circle\",\n    radius: 10,\n  })\n).toEqual({ radius: 10 });\n```\n\n## Tuples\n\n```ts\n// $ExpectType Schema\u003creadonly [string, number]\u003e\nS.tuple(S.string, S.number);\n```\n\n### Append a required element\n\n```ts\n// $ExpectType Schema\u003creadonly [string, number, boolean]\u003e\nS.tuple(S.string, S.number).pipe(S.element(S.boolean));\n```\n\n### Append an optional element\n\n```ts\n// $ExpectType Schema\u003creadonly [string, number, boolean?]\u003e\nS.tuple(S.string, S.number).pipe(S.optionalElement(S.boolean));\n```\n\n### Append a rest element\n\n```ts\n// $ExpectType Schema\u003creadonly [string, number, ...boolean[]]\u003e\nS.tuple(S.string, S.number).pipe(S.rest(S.boolean));\n```\n\n## Arrays\n\n```ts\n// $ExpectType Schema\u003creadonly number[]\u003e\nS.array(S.number);\n```\n\n### Mutable Arrays\n\nBy default, when you use `S.array`, it generates a type marked as readonly. The `mutable` combinator is a useful function for creating a new schema with a mutable type in a **shallow** manner:\n\n```ts\n// $ExpectType Schema\u003cnumber[]\u003e\nS.mutable(S.array(S.number));\n```\n\n### Non empty arrays\n\n```ts\n// $ExpectType Schema\u003creadonly [number, ...number[]]\u003e\nS.nonEmptyArray(S.number);\n```\n\n## Structs\n\n```ts\n// $ExpectType Schema\u003c{ readonly a: string; readonly b: number; }\u003e\nS.struct({ a: S.string, b: S.number });\n```\n\n### Mutable Properties\n\nBy default, when you use `S.struct`, it generates a type with properties that are marked as readonly. The `mutable` combinator is a useful function for creating a new schema with properties made mutable in a **shallow** manner:\n\n```ts\n// $ExpectType Schema\u003c{ a: string; b: number; }\u003e\nS.mutable(S.struct({ a: S.string, b: S.number }));\n```\n\n### Optional fields\n\n**Cheatsheet**\n\n| Combinator | From                              | To                                                              |\n| ---------- | --------------------------------- | --------------------------------------------------------------- |\n| `optional` | `Schema\u003cI, A\u003e`                    | `PropertySignature\u003cI \\| undefined, true, A \\| undefined, true\u003e` |\n| `optional` | `Schema\u003cI, A\u003e`, `{ exact: true }` | `PropertySignature\u003cI, true, A, true\u003e`                           |\n\n#### optional(schema)\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `\u003cmissing value\u003e`\n  - `undefined` -\u003e `undefined`\n  - `i` -\u003e `a`\n- encoding\n  - `\u003cmissing value\u003e` -\u003e `\u003cmissing value\u003e`\n  - `undefined` -\u003e `undefined`\n  - `a` -\u003e `i`\n\n#### optional(schema, { exact: true })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `\u003cmissing value\u003e`\n  - `i` -\u003e `a`\n- encoding\n  - `\u003cmissing value\u003e` -\u003e `\u003cmissing value\u003e`\n  - `a` -\u003e `i`\n\n### Default values\n\n| Combinator | From                                                                | To                                                          |\n| ---------- | ------------------------------------------------------------------- | ----------------------------------------------------------- |\n| `optional` | `Schema\u003cI, A\u003e`, `{ default: () =\u003e A }`                              | `PropertySignature\u003cI \\| undefined, true, A, false\u003e`         |\n| `optional` | `Schema\u003cI, A\u003e`, `{ exact: true, default: () =\u003e A }`                 | `PropertySignature\u003cI, true, A, false\u003e`                      |\n| `optional` | `Schema\u003cI, A\u003e`, `{ nullable: true, default: () =\u003e A }`              | `PropertySignature\u003cI \\| null \\| undefined, true, A, false\u003e` |\n| `optional` | `Schema\u003cI, A\u003e`, `{ exact: true, nullable: true, default: () =\u003e A }` | `PropertySignature\u003cI \\| null, true, A, false\u003e`              |\n\n#### optional(schema, { default: () =\u003e A })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `\u003cdefault value\u003e`\n  - `undefined` -\u003e `\u003cdefault value\u003e`\n  - `i` -\u003e `a`\n- encoding\n  - `a` -\u003e `i`\n\n#### optional(schema, { exact: true, default: () =\u003e A })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `\u003cdefault value\u003e`\n  - `i` -\u003e `a`\n- encoding\n  - `a` -\u003e `i`\n\n#### optional(schema, { nullable: true, default: () =\u003e A })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `\u003cdefault value\u003e`\n  - `undefined` -\u003e `\u003cdefault value\u003e`\n  - `null` -\u003e `\u003cdefault value\u003e`\n  - `i` -\u003e `a`\n- encoding\n  - `a` -\u003e `i`\n\n#### optional(schema, { exact: true, nullable: true, default: () =\u003e A })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `\u003cdefault value\u003e`\n  - `null` -\u003e `\u003cdefault value\u003e`\n  - `i` -\u003e `a`\n- encoding\n  - `a` -\u003e `i`\n\n### Optional fields as `Option`s\n\n| Combinator | From                                                            | To                                                                  |\n| ---------- | --------------------------------------------------------------- | ------------------------------------------------------------------- |\n| `optional` | `Schema\u003cI, A\u003e`, `{ as: \"Option\" }`                              | `PropertySignature\u003cI \\| undefined, true, Option\u003cA\u003e, false\u003e`         |\n| `optional` | `Schema\u003cI, A\u003e`, `{ exact: true, as: \"Option\" }`                 | `PropertySignature\u003cI, true, Option\u003cA\u003e, false\u003e`                      |\n| `optional` | `Schema\u003cI, A\u003e`, `{ nullable: true, as: \"Option\" }`              | `PropertySignature\u003cI \\| undefined \\| null, true, Option\u003cA\u003e, false\u003e` |\n| `optional` | `Schema\u003cI, A\u003e`, `{ exact: true, nullable: true, as: \"Option\" }` | `PropertySignature\u003cI \\| null, true, Option\u003cA\u003e, false\u003e`              |\n\n#### optional(schema, { as: \"Option\" })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `Option.none()`\n  - `undefined` -\u003e `Option.none()`\n  - `i` -\u003e `Option.some(a)`\n- encoding\n  - `Option.none()` -\u003e `\u003cmissing value\u003e`\n  - `Option.some(a)` -\u003e `i`\n\n#### optional(schema, { exact: true, as: \"Option\" })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `Option.none()`\n  - `i` -\u003e `Option.some(a)`\n- encoding\n  - `Option.none()` -\u003e `\u003cmissing value\u003e`\n  - `Option.some(a)` -\u003e `i`\n\n#### optional(schema, { nullable: true, as: \"Option\" })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `Option.none()`\n  - `undefined` -\u003e `Option.none()`\n  - `null` -\u003e `Option.none()`\n  - `i` -\u003e `Option.some(a)`\n- encoding\n  - `Option.none()` -\u003e `\u003cmissing value\u003e`\n  - `Option.some(a)` -\u003e `i`\n\n#### optional(schema, { exact: true, nullable: true, as: \"Option\" })\n\n- decoding\n  - `\u003cmissing value\u003e` -\u003e `Option.none()`\n  - `null` -\u003e `Option.none()`\n  - `i` -\u003e `Option.some(a)`\n- encoding\n  - `Option.none()` -\u003e `\u003cmissing value\u003e`\n  - `Option.some(a)` -\u003e `i`\n\n### Renaming Properties\n\nTo rename one or more properties, you can utilize the `rename` API:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// Original Schema\nconst originalSchema = S.struct({ a: S.string, b: S.number });\n\n// Renaming the \"a\" property to \"c\"\nconst renamedSchema = S.rename(originalSchema, { a: \"c\" });\n\nS.parseSync(renamedSchema)({ a: \"a\", b: 1 }); // Output: { c: \"a\", b: 1 }\n```\n\nIn the example above, we have an original schema with properties \"a\" and \"b.\" Using the `rename` API, we create a new schema where we rename the \"a\" property to \"c.\" The resulting schema, when used with `S.parseSync`, transforms the input object by renaming the specified property.\n\n## Classes\n\nWhen working with schemas, you have a choice beyond the `S.struct` constructor. You can leverage the power of classes through the `Class` utility, which comes with its own set of advantages tailored to common use cases.\n\n### The Benefits of Using Classes\n\nClasses offer several features that simplify the schema creation process:\n\n- **All-in-One Definition**: With classes, you can define both a schema and an opaque type simultaneously.\n- **Shared Functionality**: You can incorporate shared functionality using class methods or getters.\n- **Value Equality and Hashing**: Utilize the built-in capability for checking value equality and applying hashing (thanks to `Class` implementing `Data.Case`).\n\nLet's dive into an illustrative example to better understand how classes work:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// Define your schema by providing the type to `Class` and the desired fields\nclass Person extends S.Class\u003cPerson\u003e()({\n  id: S.number,\n  name: S.string.pipe(S.nonEmpty()),\n}) {}\n```\n\n### Validation and Instantiation\n\nThe class constructor serves as a validation and instantiation tool. It ensures that the provided properties meet the schema requirements:\n\n```ts\nconst tim = new Person({ id: 1, name: \"Tim\" });\n```\n\nKeep in mind that it throws an error for invalid properties:\n\n```ts\nnew Person({ id: 1, name: \"\" });\n/* throws\nerror(s) found\n└─ [\"name\"]\n   └─ Expected a non empty string, actual \"\"\n*/\n```\n\n### Custom Getters and Methods\n\nFor more flexibility, you can also introduce custom getters and methods:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nclass Person extends S.Class\u003cPerson\u003e()({\n  id: S.number,\n  name: S.string.pipe(S.nonEmpty()),\n}) {\n  get upperName() {\n    return this.name.toUpperCase();\n  }\n}\n\nconst john = new Person({ id: 1, name: \"John\" });\n\nconsole.log(john.upperName); // \"JOHN\"\n```\n\n### Accessing Related Schemas\n\nThe class constructor itself is a Schema, and can be assigned/provided anywhere a Schema is expected. There is also a `.struct` property, which can be used when the class prototype is not required.\n\n```ts\n// $ExpectType Schema\u003c{ readonly id: number; name: string; }, Person\u003e\nS.suspend(() =\u003e Person);\n\n// $ExpectType Schema\u003c{ readonly id: number; name: string; }, { readonly id: number; name: string; }\u003e\nPerson.struct;\n```\n\n### Tagged Class variants\n\nYou can also create classes that extend `TaggedClass` \u0026 `TaggedError` from the `effect/Data` module:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nclass TaggedPerson extends S.TaggedClass\u003cTaggedPerson\u003e()(\"TaggedPerson\", {\n  name: S.string,\n}) {}\n\nclass HttpError extends S.TaggedError\u003cHttpError\u003e()(\"HttpError\", {\n  status: S.number,\n}) {}\n\nconst joe = new TaggedPerson({ name: \"Joe\" });\nconsole.log(joe._tag); // \"TaggedPerson\"\n\nconst error = new HttpError({ status: 404 });\nconsole.log(error._tag); // \"HttpError\"\nconsole.log(error.stack); // access the stack trace\n```\n\n### Extending existing Classes\n\nIn situations where you need to augment your existing class with more fields, the built-in `extend` utility comes in handy:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nclass Person extends S.Class\u003cPerson\u003e()({\n  id: S.number,\n  name: S.string.pipe(S.nonEmpty()),\n}) {\n  get upperName() {\n    return this.name.toUpperCase();\n  }\n}\n\nclass PersonWithAge extends Person.extend\u003cPersonWithAge\u003e()({\n  age: S.number,\n}) {\n  get isAdult() {\n    return this.age \u003e= 18;\n  }\n}\n```\n\n### Transforms\n\nYou have the option to enhance a class with (effectful) transforms. This becomes valuable when you want to enrich or validate an entity sourced from a data store.\n\n```ts\nimport * as ParseResult from \"@effect/schema/ParseResult\";\nimport * as S from \"@effect/schema/Schema\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\n\nexport class Person extends S.Class\u003cPerson\u003e()({\n  id: S.number,\n  name: S.string,\n}) {}\n\nconsole.log(S.parseSync(Person)({ id: 1, name: \"name\" }));\n/*\nOutput:\nPerson { id: 1, name: 'name' }\n*/\n\nfunction getAge(id: number): Effect.Effect\u003cnever, Error, number\u003e {\n  return Effect.succeed(id + 2);\n}\n\nexport class PersonWithTransform extends Person.transform\u003cPersonWithTransform\u003e()(\n  {\n    age: S.optional(S.number), { exact: true, as: \"Option\" }),\n  },\n  (input) =\u003e\n    Effect.mapBoth(getAge(input.id), {\n      onFailure: (e) =\u003e\n        ParseResult.parseError([\n          ParseResult.type(S.string.ast, input.id, e.message),\n        ]),\n      // must return { age: Option\u003cnumber\u003e }\n      onSuccess: (age) =\u003e ({ ...input, age: Option.some(age) }),\n    }),\n  ParseResult.succeed\n) {}\n\nS.parsePromise(PersonWithTransform)({ id: 1, name: \"name\" }).then(console.log);\n/*\nOutput:\nPersonWithTransform {\n  id: 1,\n  name: 'name',\n  age: { _id: 'Option', _tag: 'Some', value: 3 }\n}\n*/\n\nexport class PersonWithTransformFrom extends Person.transformFrom\u003cPersonWithTransformFrom\u003e()(\n  {\n    age: S.optional(S.number, { exact: true, as: \"Option\" }),\n  },\n  (input) =\u003e\n    Effect.mapBoth(getAge(input.id), {\n      onFailure: (e) =\u003e\n        ParseResult.parseError([\n          ParseResult.type(S.string.ast, input, e.message),\n        ]),\n      // must return { age?: number }\n      onSuccess: (age) =\u003e (age \u003e 18 ? { ...input, age } : { ...input }),\n    }),\n  ParseResult.succeed\n) {}\n\nS.parsePromise(PersonWithTransformFrom)({ id: 1, name: \"name\" }).then(\n  console.log\n);\n/*\nOutput:\nPersonWithTransformFrom {\n  id: 1,\n  name: 'name',\n  age: { _id: 'Option', _tag: 'None' }\n}\n*/\n```\n\nThe decision of which API to use, either `transform` or `transformFrom`, depends on when you wish to execute the transformation:\n\n1. Using `transform`:\n\n   - The transformation occurs at the end of the process.\n   - It expects you to provide a value of type `{ age: Option\u003cnumber\u003e }`.\n   - After processing the initial input, the new transformation comes into play, and you need to ensure the final output adheres to the specified structure.\n\n2. Using `transformFrom`:\n   - The new transformation starts as soon as the initial input is handled.\n   - You should provide a value `{ age?: number }`.\n   - Based on this fresh input, the subsequent transformation `{ age: S.optionalToOption(S.number, { exact: true }) }` is executed.\n   - This approach allows for immediate handling of the input, potentially influencing the subsequent transformations.\n\n## Pick\n\nThe `pick` operation is used to select specific properties from a schema.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003c{ readonly a: string; }\u003e\nS.struct({ a: S.string, b: S.number, c: S.boolean }).pipe(S.pick(\"a\"));\n\n// $ExpectType Schema\u003c{ readonly a: string; readonly c: boolean; }\u003e\nS.struct({ a: S.string, b: S.number, c: S.boolean }).pipe(S.pick(\"a\", \"c\"));\n```\n\n## Omit\n\nThe `omit` operation is employed to exclude certain properties from a schema.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003c{ readonly b: number; readonly c: boolean; }\u003e\nS.struct({ a: S.string, b: S.number, c: S.boolean }).pipe(S.omit(\"a\"));\n\n// $ExpectType Schema\u003c{ readonly b: number; }\u003e\nS.struct({ a: S.string, b: S.number, c: S.boolean }).pipe(S.omit(\"a\", \"c\"));\n```\n\n## Partial\n\nThe `partial` operation makes all properties within a schema optional.\n\n```ts\n// $ExpectType Schema\u003c{ readonly a?: string; readonly b?: number; }\u003e\nS.partial(S.struct({ a: S.string, b: S.number }));\n```\n\n## Required\n\nThe `required` operation ensures that all properties in a schema are mandatory.\n\n```ts\n// $ExpectType Schema\u003c{ readonly a: string; readonly b: number; }\u003e\nS.required(\n  S.struct({\n    a: S.optional(S.string, { exact: true }),\n    b: S.optional(S.number, { exact: true }),\n  })\n);\n```\n\n## Records\n\n### String keys\n\n```ts\n// $ExpectType Schema\u003c{ readonly [x: string]: string; }\u003e\nS.record(S.string, S.string);\n\n// $ExpectType Schema\u003c{ readonly a: string; readonly b: string; }\u003e\nS.record(S.union(S.literal(\"a\"), S.literal(\"b\")), S.string);\n```\n\n### Keys refinements\n\n```ts\n// $ExpectType Schema\u003c{ readonly [x: string]: string; }\u003e\nS.record(S.string.pipe(S.minLength(2)), S.string);\n```\n\n### Symbol keys\n\n```ts\n// $ExpectType Schema\u003c{ readonly [x: symbol]: string; }\u003e\nS.record(S.symbolFromSelf, S.string);\n```\n\n### Template literal keys\n\n```ts\n// $ExpectType Schema\u003c{ readonly [x: `a${string}`]: string; }\u003e\nS.record(S.templateLiteral(S.literal(\"a\"), S.string), S.string);\n```\n\n### Mutable Records\n\nBy default, when you use `S.record`, it generates a type marked as readonly. The `mutable` combinator is a useful function for creating a new schema with a mutable type in a **shallow** manner:\n\n```ts\n// $ExpectType Schema\u003c{ [x: string]: string; }\u003e\nS.mutable(S.record(S.string, S.string););\n```\n\n## Extend\n\nThe `extend` combinator allows you to add additional fields or index signatures to an existing `Schema`.\n\n```ts\n// $ExpectType Schema\u003c{ readonly [x: string]: string; readonly a: string; readonly b: string; readonly c: string; }\u003e\nS.struct({ a: S.string, b: S.string }).pipe(\n  S.extend(S.struct({ c: S.string })), // \u003c= you can add more fields\n  S.extend(S.record(S.string, S.string)) // \u003c= you can add index signatures\n);\n```\n\n## Compose\n\nCombining and reusing schemas is a common requirement, the `compose` combinator allows you to do just that. It enables you to combine two schemas, `Schema\u003cA, B\u003e` and `Schema\u003cB, C\u003e`, into a single schema `Schema\u003cA, C\u003e`:\n\n```ts\n// $ExpectType Schema\u003cstring, readonly string[]\u003e\nconst schema1 = S.split(S.string, \",\");\n\n// $ExpectType Schema\u003creadonly string[], readonly number[]\u003e\nconst schema2 = S.array(S.NumberFromString);\n\n// $ExpectType Schema\u003cstring, readonly number[]\u003e\nconst composedSchema = S.compose(schema1, schema2);\n```\n\nIn this example, we have two schemas, `schema1` and `schema2`. The first schema, `schema1`, takes a string and splits it into an array using a comma as the delimiter. The second schema, `schema2`, transforms an array of strings into an array of numbers.\n\nNow, by using the `compose` combinator, we can create a new schema, `composedSchema`, that combines the functionality of both `schema1` and `schema2`. This allows us to parse a string and directly obtain an array of numbers as a result.\n\n## InstanceOf\n\nIn the following section, we demonstrate how to use the `instanceOf` combinator to create a `Schema` for a class instance.\n\n```ts\nclass Test {\n  constructor(readonly name: string) {}\n}\n\n// $ExpectType Schema\u003cTest\u003e\nS.instanceOf(Test);\n```\n\n## Recursive types\n\nThe `suspend` combinator is useful when you need to define a `Schema` that depends on itself, like in the case of recursive data structures. In this example, the `Category` schema depends on itself because it has a field `subcategories` that is an array of `Category` objects.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\ninterface Category {\n  readonly name: string;\n  readonly subcategories: ReadonlyArray\u003cCategory\u003e;\n}\n\nconst Category: S.Schema\u003cCategory\u003e = S.struct({\n  name: S.string,\n  subcategories: S.array(S.suspend(() =\u003e Category)),\n});\n```\n\nHere's an example of two mutually recursive schemas, `Expression` and `Operation`, that represent a simple arithmetic expression tree.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\ninterface Expression {\n  readonly type: \"expression\";\n  readonly value: number | Operation;\n}\n\ninterface Operation {\n  readonly type: \"operation\";\n  readonly operator: \"+\" | \"-\";\n  readonly left: Expression;\n  readonly right: Expression;\n}\n\nconst Expression: S.Schema\u003cExpression\u003e = S.struct({\n  type: S.literal(\"expression\"),\n  value: S.union(\n    S.number,\n    S.suspend(() =\u003e Operation)\n  ),\n});\n\nconst Operation: S.Schema\u003cOperation\u003e = S.struct({\n  type: S.literal(\"operation\"),\n  operator: S.union(S.literal(\"+\"), S.literal(\"-\")),\n  left: Expression,\n  right: Expression,\n});\n```\n\n## Transformations\n\nIn some cases, we may need to transform the output of a schema to a different type. For instance, we may want to parse a string into a number, or we may want to transform a date string into a `Date` object.\n\nTo perform these kinds of transformations, the `@effect/schema` library provides the `transform` combinator.\n\n### transform\n\n```ts\ndeclare const transform: \u003cA, B, C, D\u003e(\n  from: Schema\u003cA, B\u003e,\n  to: Schema\u003cC, D\u003e,\n  decode: (b: B) =\u003e C,\n  encode: (c: C) =\u003e B\n) =\u003e Schema\u003cA, D\u003e;\n```\n\n```mermaid\nflowchart TD\n  schema1[\"from: Schema\u0026lt;A, B\u0026gt;\"]\n  schema2[\"to: Schema\u0026lt;C, D\u0026gt;\"]\n  schema1--decode: B -\u003e C--\u003eschema2\n  schema2--encode: C -\u003e B--\u003eschema1\n```\n\nThe `transform` combinator takes a source schema, a target schema, a transformation function from the source type to the target type, and a reverse transformation function from the target type back to the source type. It returns a new schema that applies the transformation function to the output of the original schema before returning it. If the original schema fails to parse a value, the transformed schema will also fail.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// use the transform combinator to convert the string schema into the tuple schema\nexport const transformedSchema: S.Schema\u003cstring, readonly [string]\u003e =\n  S.transform(\n    S.string,\n    S.tuple(S.string),\n    // define a function that converts a string into a tuple with one element of type string\n    (s) =\u003e [s] as const,\n    // define a function that converts a tuple with one element of type string into a string\n    ([s]) =\u003e s\n  );\n```\n\nIn the example above, we defined a schema for the `string` type and a schema for the tuple type `[string]`. We also defined the functions `decode` and `encode` that convert a `string` into a tuple and a tuple into a `string`, respectively. Then, we used the `transform` combinator to convert the string schema into a schema for the tuple type `[string]`. The resulting schema can be used to parse values of type `string` into values of type `[string]`.\n\n#### Non-strict option\n\nIf you need to be less restrictive in your `decode` and `encode` functions, you can make use of the `{ strict: false }` option:\n\n```ts\ndeclare const transform: \u003cA, B, C, D\u003e(\n  from: Schema\u003cA, B\u003e,\n  to: Schema\u003cC, D\u003e,\n  decode: (b: B) =\u003e unknown, // Less strict constraint\n  encode: (c: C) =\u003e unknown, // Less strict constraint\n  options: { strict: false }\n) =\u003e Schema\u003cA, D\u003e;\n```\n\nThis is useful when you want to relax the type constraints imposed by the `decode` and `encode` functions, making them more permissive.\n\n### transformOrFail\n\nThe `transformOrFail` combinator works in a similar way, but allows the transformation function to return a `ParseResult` object, which can either be a success or a failure.\n\n```ts\nimport * as ParseResult from \"@effect/schema/ParseResult\";\nimport * as S from \"@effect/schema/Schema\";\n\nexport const transformedSchema: S.Schema\u003cstring, boolean\u003e = S.transformOrFail(\n  S.string,\n  S.boolean,\n  // define a function that converts a string into a boolean\n  (s) =\u003e\n    s === \"true\"\n      ? ParseResult.succeed(true)\n      : s === \"false\"\n        ? ParseResult.succeed(false)\n        : ParseResult.fail(ParseResult.type(S.literal(\"true\", \"false\").ast, s)),\n  // define a function that converts a boolean into a string\n  (b) =\u003e ParseResult.succeed(String(b))\n);\n```\n\nThe transformation may also be async:\n\n```ts\nimport * as ParseResult from \"@effect/schema/ParseResult\";\nimport * as S from \"@effect/schema/Schema\";\nimport * as TreeFormatter from \"@effect/schema/TreeFormatter\";\nimport * as Effect from \"effect/Effect\";\n\nconst api = (url: string) =\u003e\n  Effect.tryPromise({\n    try: () =\u003e\n      fetch(url).then((res) =\u003e {\n        if (res.ok) {\n          return res.json() as Promise\u003cunknown\u003e;\n        }\n        throw new Error(String(res.status));\n      }),\n    catch: (e) =\u003e new Error(String(e)),\n  });\n\nconst PeopleId = S.string.pipe(S.brand(\"PeopleId\"));\n\nconst PeopleIdFromString = S.transformOrFail(\n  S.string,\n  PeopleId,\n  (s, _, ast) =\u003e\n    Effect.mapBoth(api(`https://swapi.dev/api/people/${s}`), {\n      onFailure: (e) =\u003e\n        ParseResult.parseError([ParseResult.type(ast, s, e.message)]),\n      onSuccess: () =\u003e s,\n    }),\n  ParseResult.succeed\n);\n\nconst parse = (id: string) =\u003e\n  Effect.mapError(S.parse(PeopleIdFromString)(id), (e) =\u003e\n    TreeFormatter.formatErrors(e.errors)\n  );\n\nEffect.runPromiseExit(parse(\"1\")).then(console.log);\n/*\nOutput:\n{ _id: 'Exit', _tag: 'Success', value: '1' }\n*/\n\nEffect.runPromiseExit(parse(\"fail\")).then(console.log);\n/*\nOutput:\n{\n  _id: 'Exit',\n  _tag: 'Failure',\n  cause: {\n    _id: 'Cause',\n    _tag: 'Fail',\n    failure: 'error(s) found\\n└─ Error: 404'\n  }\n}\n*/\n```\n\n### String transformations\n\n#### split\n\nThe `split` combinator allows splitting a string into an array of strings.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, string[]\u003e\nconst schema = S.string.pipe(S.split(\",\"));\nconst parse = S.parseSync(schema);\n\nparse(\"\"); // [\"\"]\nparse(\",\"); // [\"\", \"\"]\nparse(\"a,\"); // [\"a\", \"\"]\nparse(\"a,b\"); // [\"a\", \"b\"]\n```\n\n#### Trim\n\nThe `Trim` schema allows removing whitespaces from the beginning and end of a string.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, string\u003e\nconst schema = S.Trim;\nconst parse = S.parseSync(schema);\n\nparse(\"a\"); // \"a\"\nparse(\" a\"); // \"a\"\nparse(\"a \"); // \"a\"\nparse(\" a \"); // \"a\"\n```\n\n**Note**. If you were looking for a combinator to check if a string is trimmed, check out the `trimmed` combinator.\n\n#### Lowercase\n\nThe `Lowercase` schema converts a string to lowercase.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// const schema: S.Schema\u003cstring, string\u003e\nconst schema = S.Lowercase;\nconst parse = S.parseSync(schema);\n\nparse(\"A\"); // \"a\"\nparse(\" AB\"); // \" ab\"\nparse(\"Ab \"); // \"ab \"\nparse(\" ABc \"); // \" abc \"\n```\n\n**Note**. If you were looking for a combinator to check if a string is lowercased, check out the `lowercased` combinator.\n\n#### ParseJson\n\nThe `ParseJson` schema offers a method to convert JSON strings into the `unknown` type using the underlying functionality of `JSON.parse`. It also employs `JSON.stringify` for encoding.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, unknown\u003e\nconst schema = S.ParseJson;\nconst parse = S.parseSync(schema);\n\nparse(\"{}\"); // {}\nparse(`{\"a\":\"b\"}`); // { \"a\": \"b\" }\nparse(\"\"); // throws Unexpected end of JSON input\n```\n\nYou can also compose the `ParseJson` schema with other schemas to refine the parsing result:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, { readonly a: number; }\u003e\nconst schema = S.ParseJson.pipe(S.compose(S.struct({ a: S.number })));\n```\n\nIn this example, we've composed the `ParseJson` schema with a struct schema to ensure that the result will have a specific shape, including an object with a numeric property \"a\".\n\nAlternatively, you can achieve the same result by using the equivalent built-in combinator `fromJson`:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, { readonly a: number; }\u003e\nconst schema = S.fromJson(S.struct({ a: S.number }));\n```\n\n### Number transformations\n\n#### NumberFromString\n\nTransforms a `string` into a `number` by parsing the string using `parseFloat`.\n\nThe following special string values are supported: \"NaN\", \"Infinity\", \"-Infinity\".\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, number\u003e\nconst schema = S.NumberFromString;\nconst parse = S.parseSync(schema);\n\n// success cases\nparse(\"1\"); // 1\nparse(\"-1\"); // -1\nparse(\"1.5\"); // 1.5\nparse(\"NaN\"); // NaN\nparse(\"Infinity\"); // Infinity\nparse(\"-Infinity\"); // -Infinity\n\n// failure cases\nparse(\"a\"); // throws\n```\n\n#### clamp\n\nClamps a `number` between a minimum and a maximum value.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cnumber, number\u003e\nconst schema = S.number.pipe(S.clamp(-1, 1)); // clamps the input to -1 \u003c= x \u003c= 1\n\nconst parse = S.parseSync(schema);\nparse(-3); // -1\nparse(0); // 0\nparse(3); // 1\n```\n\n### BigDecimal transformations\n\n#### BigDecimal\n\nTransforms a `string` into a `BigDecimal`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, BigDecimal\u003e\nconst schema = S.BigDecimal;\nconst parse = S.parseSync(schema);\n\nparse(\".124\"); // BigDecimal(.124)\n```\n\n#### BigDecimalFromNumber\n\nTransforms a `number` into a `BigDecimal`.\n\n\u003e [!WARNING]\n\u003e Warning: When encoding, this Schema will produce incorrect results if the BigDecimal exceeds the 64-bit range of a number.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cnumber, BigDecimal\u003e\nconst schema = S.BigDecimalFromNumber;\nconst parse = S.parseSync(schema);\n\nparse(0.111); // BigDecimal(.111)\n```\n\n#### clampBigDecimal\n\nClamps a `BigDecimal` between a minimum and a maximum value.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as BigDecimal from \"effect/BigDecimal\";\n\n// $ExpectType Schema\u003cBigDecimal.BigDecimal, BigDecimal.BigDecimal\u003e\nconst schema = S.BigDecimal.pipe(\n  S.clampBigDecimal(BigDecimal.fromNumber(-1), BigDecimal.fromNumber(1))\n);\n\nconst parse = S.parseSync(schema);\nparse(\"-2\"); // BigDecimal(-1)\nparse(\"0\"); // BigDecimal(0)\nparse(\"3\"); // BigDecimal(1)\n```\n\n#### negateBigDecimal\n\nNegates a `BigDecimal`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as BigDecimal from \"effect/BigDecimal\";\n\n// $ExpectType Schema\u003cBigDecimal.BigDecimal, BigDecimal.BigDecimal\u003e\nconst schema = S.BigDecimal.pipe(S.negateBigDecimal);\n\nconst parse = S.parseSync(schema);\nparse(\"-2\"); // BigDecimal(2)\nparse(\"0\"); // BigDecimal(0)\nparse(\"3\"); // BigDecimal(-3)\n```\n\n### Duration transformations\n\n#### Duration\n\nConverts an hrtime(i.e. `[seconds: number, nanos: number]`) into a `Duration`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Duration from \"effect/Duration\";\n\n// $ExpectType Schema\u003cnumber, Duration\u003e\nconst schema = S.Duration;\n\nconst parse = S.parseSync(schema);\nparse([0, 0]); // 0 seconds\nparse([5000, 0]); // 5 seconds\n```\n\n#### DurationFromNumber\n\nConverts a `number` into a `Duration` where the number represents the number of milliseconds.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Duration from \"effect/Duration\";\n\n// $ExpectType Schema\u003cnumber, Duration\u003e\nconst schema = S.DurationFromNumber;\n\nconst parse = S.parseSync(schema);\nparse(0); // 0 seconds\nparse(5000); // 5 seconds\n```\n\n#### DurationFromBigint\n\nConverts a `bigint` into a `Duration` where the number represents the number of nanoseconds.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Duration from \"effect/Duration\";\n\n// $ExpectType Schema\u003cbigint, Duration\u003e\nconst schema = S.DurationFromBigint;\n\nconst parse = S.parseSync(schema);\nparse(0n); // 0 seconds\nparse(5000000000n); // 5 seconds\n```\n\n#### clampDuration\n\nClamps a `Duration` between a minimum and a maximum value.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Duration from \"effect/Duration\";\n\n// $ExpectType Schema\u003cDuration.Duration, Duration.Duration\u003e\nconst schema = S.DurationFromSelf.pipe(\n  S.clampDuration(\"5 seconds\", \"10 seconds\")\n);\n\nconst parse = S.parseSync(schema);\nparse(Duration.decode(\"2 seconds\")); // 5 seconds\nparse(Duration.decode(\"6 seconds\")); // 6 seconds\nparse(Duration.decode(\"11 seconds\")); // 10 seconds\n```\n\n### Secret transformations\n\n#### Secret\n\nConverts a `string` into a `Secret`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as Secret from \"effect/Secret\";\n\n// $ExpectType Schema\u003cstring, Secret\u003e\nconst schema = S.Secret;\n\nconst parse = S.parseSync(schema);\nparse(\"keep it secret, keep it safe\"); // Secret\n```\n\n### Symbol transformations\n\n#### symbol\n\nTransforms a `string` into a `symbol` by parsing the string using `Symbol.for`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, symbol\u003e\nconst schema = S.symbol;\nconst parse = S.parseSync(schema);\n\nparse(\"a\"); // Symbol.for(\"a\")\n```\n\n### Bigint transformations\n\n#### bigint\n\nTransforms a `string` into a `bigint` by parsing the string using `BigInt`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, bigint\u003e\nconst schema = S.bigint;\nconst parse = S.parseSync(schema);\n\n// success cases\nparse(\"1\"); // 1n\nparse(\"-1\"); // -1n\n\n// failure cases\nparse(\"a\"); // throws\nparse(\"1.5\"); // throws\nparse(\"NaN\"); // throws\nparse(\"Infinity\"); // throws\nparse(\"-Infinity\"); // throws\n```\n\n#### BigintFromNumber\n\nTransforms a `number` into a `bigint` by parsing the number using `BigInt`.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// const schema: S.Schema\u003cnumber, bigint\u003e\nconst schema = S.BigintFromNumber;\nconst parse = S.parseSync(schema);\nconst encode = S.encodeSync(schema);\n\n// success cases\nparse(1); // 1n\nparse(-1); // -1n\nencode(1n); // 1\nencode(-1n); // -1\n\n// failure cases\nparse(1.5); // throws\nparse(NaN); // throws\nparse(Infinity); // throws\nparse(-Infinity); // throws\nencode(BigInt(Number.MAX_SAFE_INTEGER) + 1n); // throws\nencode(BigInt(Number.MIN_SAFE_INTEGER) - 1n); // throws\n```\n\n#### clamp\n\nClamps a `bigint` between a minimum and a maximum value.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cbigint, bigint\u003e\nconst schema = S.bigint.pipe(S.clampBigint(-1n, 1n)); // clamps the input to -1n \u003c= x \u003c= 1n\n\nconst parse = S.parseSync(schema);\nparse(-3n); // -1n\nparse(0n); // 0n\nparse(3n); // 1n\n```\n\n### Boolean transformations\n\n#### not\n\nNegates a boolean value.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cboolean, boolean\u003e\nconst schema = S.boolean.pipe(S.not);\n\nconst parse = S.parseSync(schema);\nparse(true); // false\nparse(false); // true\n```\n\n### Date transformations\n\n#### Date\n\nTransforms a `string` into a **valid** `Date`, ensuring that invalid dates, such as `new Date(\"Invalid Date\")`, are rejected.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003cstring, Date\u003e\nconst schema = S.Date;\nconst parse = S.parseSync(schema);\n\nparse(\"1970-01-01T00:00:00.000Z\"); // new Date(0)\n\nparse(\"a\"); // throws\n\nconst validate = S.validateSync(schema);\n\nvalidate(new Date(0)); // new Date(0)\nvalidate(new Date(\"Invalid Date\")); // throws\n```\n\n## Interop with `effect/Data`\n\nThe `effect/Data` module in the Effect ecosystem serves as a utility module that simplifies the process of comparing values for equality without the need for explicit implementations of the `Equal` and `Hash` interfaces. It provides convenient APIs that automatically generate default implementations for equality checks, making it easier for developers to perform equality comparisons in their applications.\n\n```ts\nimport * as Data from \"effect/Data\";\nimport * as Equal from \"effect/Equal\";\n\nconst person1 = Data.struct({ name: \"Alice\", age: 30 });\nconst person2 = Data.struct({ name: \"Alice\", age: 30 });\n\nconsole.log(Equal.equals(person1, person2)); // true\n```\n\nYou can use the `Schema.data(schema)` combinator to build a schema from an existing schema that can decode a value `A` to a value `Data\u003cA\u003e`:\n\n```ts\n/*\nS.Schema\u003c{\n    readonly name: string;\n    readonly age: number;\n}, Data.Data\u003c{\n    readonly name: string;\n    readonly age: number;\n}\u003e\u003e\n*/\nconst schema = S.data(\n  S.struct({\n    name: S.string,\n    age: S.number,\n  })\n);\n\nconst decode = S.decode(schema);\n\nconst person1 = decode({ name: \"Alice\", age: 30 });\nconst person2 = decode({ name: \"Alice\", age: 30 });\n\nconsole.log(Equal.equals(person1, person2)); // true\n```\n\n## Option\n\n**Cheatsheet**\n\n| Combinator           | From                                | To                                          |\n| -------------------- | ----------------------------------- | ------------------------------------------- |\n| `option`             | `Schema\u003cI, A\u003e`                      | `Schema\u003cOptionFrom\u003cI\u003e, Option\u003cA\u003e\u003e`          |\n| `optionFromSelf`     | `Schema\u003cI, A\u003e`                      | `Schema\u003cOption\u003cI\u003e, Option\u003cA\u003e\u003e`              |\n| `optionFromNullable` | `Schema\u003cI, A\u003e`                      | `Schema\u003cI \\| null, Option\u003cA\u003e\u003e`              |\n| `optionFromNullish`  | `Schema\u003cI, A\u003e`, `null \\| undefined` | `Schema\u003cI \\| null \\| undefined, Option\u003cA\u003e\u003e` |\n\nwhere\n\n```ts\ntype OptionFrom\u003cI\u003e =\n  | {\n      readonly _tag: \"None\";\n    }\n  | {\n      readonly _tag: \"Some\";\n      readonly value: I;\n    };\n```\n\n### option(schema)\n\n- decoding\n  - `{ _tag: \"None\" }` -\u003e `Option.none()`\n  - `{ _tag: \"Some\", value: i }` -\u003e `Option.some(a)`\n- encoding\n  - `Option.none()` -\u003e `{ _tag: \"None\" }`\n  - `Option.some(a)` -\u003e `{ _tag: \"Some\", value: i }`\n\n### optionFromSelf(schema)\n\n- decoding\n  - `Option.none()` -\u003e `Option.none()`\n  - `Option.some(i)` -\u003e `Option.some(a)`\n- encoding\n  - `Option.none()` -\u003e `Option.none()`\n  - `Option.some(a)` -\u003e `Option.some(i)`\n\n### optionFromNullable(schema)\n\n- decoding\n  - `null` -\u003e `Option.none()`\n  - `i` -\u003e `Option.some(a)`\n- encoding\n  - `Option.none()` -\u003e `null`\n  - `Option.some(a)` -\u003e `i`\n\n### optionFromNullish(schema, onNoneEncoding)\n\n- decoding\n  - `null` -\u003e `Option.none()`\n  - `undefined` -\u003e `Option.none()`\n  - `i` -\u003e `Option.some(a)`\n- encoding\n  - `Option.none()` -\u003e `\u003conNoneEncoding value\u003e`\n  - `Option.some(a)` -\u003e `i`\n\n## ReadonlySet\n\nIn the following section, we demonstrate how to use the `readonlySet` combinator to parse a `ReadonlySet` from an array of values.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003creadonly number[], ReadonlySet\u003cnumber\u003e\u003e\nconst schema = S.readonlySet(S.number); // define a schema for ReadonlySet with number values\nconst parse = S.parseSync(schema);\n\nparse([1, 2, 3]); // new Set([1, 2, 3])\n```\n\n## ReadonlyMap\n\nIn the following section, we demonstrate how to use the `readonlyMap` combinator to parse a `ReadonlyMap` from an array of entries.\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\n// $ExpectType Schema\u003creadonly (readonly [number, string])[], ReadonlyMap\u003cnumber, string\u003e\u003e\nconst schema = S.readonlyMap(S.number, S.string); // define the schema for ReadonlyMap with number keys and string values\nconst parse = S.parseSync(schema);\n\nparse([\n  [1, \"a\"],\n  [2, \"b\"],\n  [3, \"c\"],\n]); // new Map([[1, \"a\"], [2, \"b\"], [3, \"c\"]])\n```\n\n# Technical overview\n\n## Understanding Schemas\n\nA schema is a description of a data structure that can be used to generate various artifacts from a single declaration.\n\nFrom a technical point of view a schema is just a typed wrapper of an `AST` value:\n\n```ts\ninterface Schema\u003cI, A\u003e {\n  readonly ast: AST;\n}\n```\n\nThe `AST` type represents a tiny portion of the TypeScript AST, roughly speaking the part describing ADTs (algebraic data types),\ni.e. products (like structs and tuples) and unions, plus a custom transformation node.\n\nThis means that you can define your own schema constructors / combinators as long as you are able to manipulate the `AST` value accordingly, let's see an example.\n\nSay we want to define a `pair` schema constructor, which takes a `Schema\u003cA\u003e` as input and returns a `Schema\u003creadonly [A, A]\u003e` as output.\n\nFirst of all we need to define the signature of `pair`\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\ndeclare const pair: \u003cA\u003e(schema: S.Schema\u003cA\u003e) =\u003e S.Schema\u003creadonly [A, A]\u003e;\n```\n\nThen we can implement the body using the APIs exported by the `@effect/schema/AST` module:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\nimport * as AST from \"@effect/schema/AST\";\nimport * as Option from \"effect/Option\";\n\nconst pair = \u003cA\u003e(schema: S.Schema\u003cA\u003e): S.Schema\u003creadonly [A, A]\u003e =\u003e {\n  const element = AST.createElement(\n    schema.ast, // \u003c= the element type\n    false // \u003c= is optional?\n  );\n  const tuple = AST.createTuple(\n    [element, element], // \u003c= elements definitions\n    Option.none(), // \u003c= rest element\n    true // \u003c= is readonly?\n  );\n  return S.make(tuple); // \u003c= wrap the AST value in a Schema\n};\n```\n\nThis example demonstrates the use of the low-level APIs of the `AST` module, however, the same result can be achieved more easily and conveniently by using the high-level APIs provided by the `Schema` module.\n\n```ts\nconst pair = \u003cA\u003e(schema: S.Schema\u003cA\u003e): S.Schema\u003creadonly [A, A]\u003e =\u003e\n  S.tuple(schema, schema);\n```\n\n## Annotations\n\nOne of the fundamental requirements in the design of `@effect/schema` is that it is extensible and customizable. Customizations are achieved through \"annotations\". Each node contained in the AST of `@effect/schema/AST` contains an `annotations: Record\u003csymbol, unknown\u003e` field that can be used to attach additional information to the schema.\n\nLet's see some examples:\n\n```ts\nimport * as S from \"@effect/schema/Schema\";\n\nconst Password =\n  // initial schema, a string\n  S.string.pipe(\n    // add an error message for non-string values (annotation)\n    S.message(() =\u003e \"not a string\"),\n    // add a constraint to the schema, only non-empty strings are valid\n    // and add an error message for empty strings (annotation)\n    S.nonEmpty({ message: () =\u003e \"required\" }),\n    // add a constraint to the schema, only strings with a length less or equal than 10 are valid\n    // and add an error message for strings that are too long (annotation)\n    S.maxLength(10, { message: (s) =\u003e `${s} is too long` }),\n    // add an identifier to the schema (annotation)\n    S.identifier(\"Password\"),\n    // add a title to the schema (annotation)\n    S.title(\"password\"),\n    // add a description to the schema (annotation)\n    S.description(\n      \"A password is a string of characters used to verify the identity of a user during the authentication process\"\n    ),\n    // add examples to the schema (annotation)\n    S.examples([\"1Ki77y\", \"jelly22fi$h\"]),\n    // add documentation to the schema (annotation)\n    S.documentation(`\n    jsDoc documentation...\n  `)\n  );\n```\n\nThe example shows some built-in combinators to add meta information, but users can easily add their own meta information by defining a custom combinator.\n\nHere's an example of how to add a `deprecated` annotation:\n\n```ts\nimport * as AST from \"@effect/schema/AST\";\nimport * as S from \"@effect/schema/Schema\";\n\nconst DeprecatedId = Symbol.for(\n  \"some/unique/identifier/for/the/custom/annotation\"\n);\n\nconst deprecated = \u003cI, A\u003e(self: S.Schema\u003cI, A\u003e): S.Schema\u003cI, A\u003e =\u003e\n  S.make(AST.setAnnotation(self.ast, DeprecatedId, true));\n\nconst schema = deprecated(S.string);\n\nconsole.log(schema);\n/*\nOutput:\n{\n  ast: {\n    _tag: 'StringKeyword',\n    annotations: {\n      [Symbol(@effect/schema/annotation/Title)]: 'string',\n      [Symbol(@effect/schema/annotation/Description)]: 'a string',\n      [Symbol(some/unique/identifier/for/the/custom/annotation)]: true\n    }\n  }\n  ...\n}\n*/\n```\n\nAnnotations can be read using the `getAnnotation` helper, here's an example:\n\n```ts\nimport * as Option from \"effect/Option\";\n\nconst isDeprecated = \u003cA\u003e(schema: S.Schema\u003cA\u003e): boolean =\u003e\n  AST.getAnnotation\u003cboolean\u003e(DeprecatedId)(schema.ast).pipe(\n    Option.getOrElse(() =\u003e false)\n  );\n\nconsole.log(isDeprecated(S.string)); // false\nconsole.log(isDeprecated(schema)); // true\n```\n\n# Documentation\n\n- [API Reference](https://effect-ts.github.io/schema/)\n\n# License\n\nThe MIT License (MIT)\n\n# Contributing Guidelines\n\nThank you for considering contributing to our project! Here are some guidelines to help you get started:\n\n## Reporting Bugs\n\nIf you have found a bug, please open an issue on our [issue tracker](https://github.com/Effect-TS/schema/issues) and provide as much detail as possible. This should include:\n\n- A clear and concise description of the problem\n- Steps to reproduce the problem\n- The expected behavior\n- The actual behavior\n- Any relevant error messages or logs\n\n## Suggesting Enhancements\n\nIf you have an idea for an enhancement or a new feature, please open an issue on our [issue tracker](https://github.com/Effect-TS/schema/issues) and provide as much detail as possible. This should include:\n\n- A clear and concise description of the enhancement or feature\n- Any potential benefits or use cases\n- Any potential drawbacks or trade-offs\n\n## Pull Requests\n\nWe welcome contributions via pull requests! Here are some guidelines to help you get started:\n\n1. Fork the repository and clone it to your local machine.\n2. Create a new branch for your changes: `git checkout -b my-new-feature`\n3. Ensure you have the required dependencies installed by running: `pnpm install` (assuming pnpm version `8.x`).\n4. Make your desired changes and, if applicable, include tests to validate your modifications.\n5. Run the following commands to ensure the integrity of your changes:\n   - `pnpm check`: Verify that the code compiles.\n   - `pnpm test`: Execute the tests.\n   - `pnpm circular`: Confirm there are no circular imports.\n   - `pnpm lint`: Check for code style adherence (if you happen to encounter any errors during this process, you can add the `--fix` option to automatically fix some of these style issues).\n   - `pnpm dtslint`: Run type-level tests.\n   - `pnpm docgen`: Update the automatically generated documentation.\n6. Create a changeset for your changes: before committing your changes, create a changeset to document the modifications. This helps in tracking and communicating the changes effectively. To create a changeset, run the following command: `pnpm changeset`.\n7. Commit your changes: after creating the changeset, commit your changes with a descriptive commit message: `git commit -am 'Add some feature'`.\n8. Push your changes to your fork: `git push origin my-new-feature`.\n9. Open a pull request against our `main` branch.\n\n### Pull Request Guidelines\n\n- Please make sure your changes are consistent with the project's existing style and conventions.\n- Please write clear commit messages and include a summary of your changes in the pull request description.\n- Please make sure all tests pass and add new tests as necessary.\n- If your change requires documentation, please update the relevant documentation.\n- Please be patient! We will do our best to review your pull request as soon as possible.\n\n## License\n\nBy contributing to this project, you agree that your contributions will be licensed under the project's [MIT License](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEffect-TS%2Fschema","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FEffect-TS%2Fschema","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FEffect-TS%2Fschema/lists"}