{"success":true,"data":{"web":[{"url":"https://github.com/arktypeio/arktype/issues/1110","title":"Allow string format as metadata associated with JSON ...","description":"Add format as a metadata key. This would have no effect on validation but would allow custom types like string.date that rely on non-serializable predicates.","position":1,"category":"github","markdown":"[Skip to content](https://github.com/arktypeio/arktype/issues/1110#start-of-content)\n\nYou signed in with another tab or window. [Reload](https://github.com/arktypeio/arktype/issues/1110) to refresh your session.You signed out in another tab or window. [Reload](https://github.com/arktypeio/arktype/issues/1110) to refresh your session.You switched accounts on another tab or window. [Reload](https://github.com/arktypeio/arktype/issues/1110) to refresh your session.Dismiss alert\n\n{{ message }}\n\n[arktypeio](https://github.com/arktypeio)/ **[arktype](https://github.com/arktypeio/arktype)** Public\n\n- [Sponsor](https://github.com/sponsors/arktypeio)\n- [Notifications](https://github.com/login?return_to=%2Farktypeio%2Farktype) You must be signed in to change notification settings\n- [Fork\\\\\n139](https://github.com/login?return_to=%2Farktypeio%2Farktype)\n- [Star\\\\\n7.7k](https://github.com/login?return_to=%2Farktypeio%2Farktype)\n\n\n# Allow string format as metadata associated with JSON schema\\#1110\n\n[New issue](https://github.com/login?return_to=https://github.com/arktypeio/arktype/issues/1110)\n\nCopy link\n\n[New issue](https://github.com/login?return_to=https://github.com/arktypeio/arktype/issues/1110)\n\nCopy link\n\nClosed\n\nClosed\n\n[Allow string format as metadata associated with JSON schema](https://github.com/arktypeio/arktype/issues/1110#top)#1110\n\nCopy link\n\nLabels\n\n[confirmedThe maintainers of the repo would like to address this](https://github.com/arktypeio/arktype/issues?q=state%3Aopen%20label%3A%22confirmed%22) The maintainers of the repo would like to address this\n\n[![@semohr](https://avatars.githubusercontent.com/u/39738318?v=4&size=80)](https://github.com/semohr)\n\n## Description\n\n[![@semohr](https://avatars.githubusercontent.com/u/39738318?v=4&size=48)](https://github.com/semohr)\n\n[semohr](https://github.com/semohr)\n\nopened [on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110#issue-2497352681) · edited by [ssalbdivad](https://github.com/ssalbdivad)\n\nEdits\n\nIssue body actions\n\n# Issue\n\nWhen using the `string.date.parse` type in combination with `toJsonSchema` I expected the type to be parsed to string, maybe even with the format keyword (see [json-schema](https://json-schema.org/understanding-json-schema/reference/string#dates-and-times)).\n\n## Example\n\n```\nimport {type} from \"arktype\";\n\nconst date = type(\"string.parse.date\");\ndate.in.toJsonSchema(); // <-- Errors\n```\n\nThis errors with `Uncaught ParseError: Predicate $ark.isParsableDate is not convertible to JSON Schema`.\n\nExpected output:\n\n```\n{\n\"type\":string\",\n \"format\": \"date\"\n}\n```\n\n## Solution (proposed by [@ssalbdivad](https://github.com/ssalbdivad))\n\nAdd `format` as a metadata key. This would have no effect on validation but would allow custom types like `string.date` that rely on non-serializable predicates in the type system to be converted to JSON schema.\n\nThe `format` key should be added to the output JSON schema alongside any other constraints. It should be specifically added to the non-serializable conditions it should replace, in this case a predicate for validating whether a string can be parsed as a Date. If multiple format constraints exist on the same IntersectionNode, they must be identical.\n\n## Activity\n\n[![](https://avatars.githubusercontent.com/in/235829?s=64&v=4)github-project-automation](https://github.com/apps/github-project-automation)\n\nadded this to [arktypeio](https://github.com/orgs/arktypeio/projects/4) [on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110#event-14793879377)\n\n[![](https://avatars.githubusercontent.com/in/235829?s=64&v=4)github-project-automation](https://github.com/apps/github-project-automation)\n\nmoved this to To do in [arktypeio](https://github.com/orgs/arktypeio/projects/4) [on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110)\n\n[![semohr](https://avatars.githubusercontent.com/u/39738318?v=4&size=80)](https://github.com/semohr)\n\n### semohr commented on Aug 30, 2024on Aug 30, 2024\n\n[![@semohr](https://avatars.githubusercontent.com/u/39738318?v=4&size=48)](https://github.com/semohr)\n\n[semohr](https://github.com/semohr)\n\n[on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110#issuecomment-2321476551)\n\nAuthor\n\nMore actions\n\nI had a small look into the source and you could probably just quickly patch it by adding\n\n```\nparsableDate.toJsonSchema = () => ({\n\"anyOf\": [\\\n {\\\n \"type\": \"string\",\\\n \"format\": \"date-time\",\\\n },\\\n {\\\n \"type\": \"string\",\\\n \"format\": \"date\",\\\n }\\\n]\n})\n```\n\ninto `ark/schema/type/keywords/string/date`.\n\nI'm not sure if this is where you want to do that, further you probably want some tests for that.\n\n[![ssalbdivad](https://avatars.githubusercontent.com/u/10645823?u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4&size=80)](https://github.com/ssalbdivad)\n\n### ssalbdivad commented on Aug 30, 2024on Aug 30, 2024\n\n[![@ssalbdivad](https://avatars.githubusercontent.com/u/10645823?u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4&size=48)](https://github.com/ssalbdivad)\n\n[ssalbdivad](https://github.com/ssalbdivad)\n\n[on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110#issuecomment-2321645099)\n\nMember\n\nMore actions\n\nI'll have to think about this a bit since `format` does not fit into the type system the same way `pattern`, `divisor`, etc. do.\n\nIf you're okay with a specific date format like ISO8601, you can use something like `string.date.iso.parse`:\n\n```\nconst user = type({\n\tname: \"string\",\n\tbirthday: \"string.date.iso.parse\"\n})\n\nconst schema = user.in.toJsonSchema()\n\nconst result = {\n\ttype: \"object\",\n\tproperties: {\n\t\tbirthday: {\n\t\t\ttype: \"string\",\n\t\t\tpattern:\n\t\t\t\t\"^([+-]?\\\\d{4}(?!\\\\d{2}\\\\b))((-?)((0[1-9]|1[0-2])(\\\\3([12]\\\\d|0[1-9]|3[01]))?|W([0-4]\\\\d|5[0-3])(-?[1-7])?|(00[1-9]|0[1-9]\\\\d|[12]\\\\d{2}|3([0-5]\\\\d|6[1-6])))([T]((([01]\\\\d|2[0-3])((:?)[0-5]\\\\d)?|24:?00)([.,]\\\\d+(?!:))?)?(\\\\17[0-5]\\\\d([.,]\\\\d+)?)?([zZ]|([+-])([01]\\\\d|2[0-3]):?([0-5]\\\\d)?)?)?)?$\"\n\t\t},\n\t\tname: { type: \"string\" }\n\t},\n\trequired: [\"birthday\", \"name\"]\n}\n```\n\nI think having a way to add \"format\" as metadata to a string type though would be useful, so I'm going tweak this issue a bit to reflect the broader goal.\n\n👍React with 👍1semohr\n\n[![](https://avatars.githubusercontent.com/u/10645823?s=64&u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4)ssalbdivad](https://github.com/ssalbdivad)\n\nchanged the title ~~\\[-\\]Allow toJschonSchema of \"string.date.parse\"\\[/-\\]~~\\[+\\]Allow string format as metadata associated with JSON schema\\[/+\\] [on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110#event-14083317804)\n\n[![](https://avatars.githubusercontent.com/u/10645823?s=64&u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4)ssalbdivad](https://github.com/ssalbdivad)\n\nadded\n\n[confirmedThe maintainers of the repo would like to address this](https://github.com/arktypeio/arktype/issues?q=state%3Aopen%20label%3A%22confirmed%22) The maintainers of the repo would like to address this\n\n[on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110#event-14083522136)\n\n[![semohr](https://avatars.githubusercontent.com/u/39738318?v=4&size=80)](https://github.com/semohr)\n\n### semohr commented on Aug 30, 2024on Aug 30, 2024\n\n[![@semohr](https://avatars.githubusercontent.com/u/39738318?v=4&size=48)](https://github.com/semohr)\n\n[semohr](https://github.com/semohr)\n\n[on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110#issuecomment-2321751622)\n\nAuthor\n\nMore actions\n\nIn theory format date or date-time means it complies with the [RFC 3339](https://datatracker.ietf.org/doc/html/rfc3339) specification. I'm not sure if the current `string.date.parse` functionalities fulfill the specification so using the regex might event be the correct choice.\n\n[![ssalbdivad](https://avatars.githubusercontent.com/u/10645823?u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4&size=80)](https://github.com/ssalbdivad)\n\n### ssalbdivad commented on Aug 30, 2024on Aug 30, 2024\n\n[![@ssalbdivad](https://avatars.githubusercontent.com/u/10645823?u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4&size=48)](https://github.com/ssalbdivad)\n\n[ssalbdivad](https://github.com/ssalbdivad)\n\n[on Aug 30, 2024on Aug 30, 2024](https://github.com/arktypeio/arktype/issues/1110#issuecomment-2321767229)\n\nMember\n\nMore actions\n\nYeah I can likely create some new keywords to specifically align with these standards, but generally these subtypes are stricter the further you chain them. `string.date` should be the most permissive, so it allows anything that can be parsed via `new Date()` that doesn't result in an invalid date.\n\nOnce we have the capability to associate JSON schema format as metadata, we'll have to ensure all the other format keywords are integrated with the type system as well.\n\n[![](https://avatars.githubusercontent.com/u/10645823?s=64&u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4)ssalbdivad](https://github.com/ssalbdivad)\n\nmentioned this [on Jan 28, 2025on Jan 28, 2025](https://github.com/arktypeio/arktype/issues/1110#event-1979850897)\n\n- [type `toJsonSchema()` does not include information about value defaults #1279](https://github.com/arktypeio/arktype/issues/1279)\n\n\n[![](https://avatars.githubusercontent.com/u/10645823?s=64&u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4)ssalbdivad](https://github.com/ssalbdivad)\n\nmoved this from Backlog to Planned in [arktypeio](https://github.com/orgs/arktypeio/projects/4) [on Feb 1, 2025on Feb 1, 2025](https://github.com/arktypeio/arktype/issues/1110)\n\n[![jacksteamdev](https://avatars.githubusercontent.com/u/23390212?v=4&size=80)](https://github.com/jacksteamdev)\n\n### jacksteamdev commented on Feb 20, 2025on Feb 20, 2025\n\n[![@jacksteamdev](https://avatars.githubusercontent.com/u/23390212?v=4&size=48)](https://github.com/jacksteamdev)\n\n[jacksteamdev](https://github.com/jacksteamdev)\n\n[on Feb 20, 2025on Feb 20, 2025](https://github.com/arktypeio/arktype/issues/1110#issuecomment-2672670384)\n\nMore actions\n\nAs the [Temporal API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal/ZonedDateTime#rfc_9557_format) starts to roll out (it's available now behind flags in Node, Deno and Bun), it will be nice to have [RFC 9557](https://datatracker.ietf.org/doc/html/rfc9557#name-format-of-extended-informat) support:\n\n```\n2025-02-20T20:39:03-05:00[America/New_York]\n2025-02-20T20:39:03Z[America/New_York]\n2025-02-20T20:39:03-05:00[America/New_York][foo=bar]\n```\n\n[![](https://avatars.githubusercontent.com/u/10645823?s=64&u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4)ssalbdivad](https://github.com/ssalbdivad)\n\nmoved this from Planned to Implemented in [arktypeio](https://github.com/orgs/arktypeio/projects/4) [on Apr 16, 2025on Apr 16, 2025](https://github.com/arktypeio/arktype/issues/1110)\n\n[![](https://avatars.githubusercontent.com/u/10645823?s=64&u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4)ssalbdivad](https://github.com/ssalbdivad)\n\nmentioned this [on Apr 16, 2025on Apr 16, 2025](https://github.com/arktypeio/arktype/issues/1110#event-2330423610)\n\n- [toJsonSchema config, cyclic discriminated union fixes #1425](https://github.com/arktypeio/arktype/pull/1425)\n\n\n[![](https://avatars.githubusercontent.com/u/10645823?s=64&u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4)ssalbdivad](https://github.com/ssalbdivad)\n\nclosed this as [completed](https://github.com/arktypeio/arktype/issues?q=is%3Aissue%20state%3Aclosed%20archived%3Afalse%20reason%3Acompleted) [on Apr 16, 2025on Apr 16, 2025](https://github.com/arktypeio/arktype/issues/1110#event-17290428153)\n\n[![](https://avatars.githubusercontent.com/in/235829?s=64&v=4)github-project-automation](https://github.com/apps/github-project-automation)\n\nmoved this from Implemented to Done (merged or closed) in [arktypeio](https://github.com/orgs/arktypeio/projects/4) [on Apr 16, 2025on Apr 16, 2025](https://github.com/arktypeio/arktype/issues/1110)\n\n[![](https://avatars.githubusercontent.com/in/347564?s=64&v=4)coderabbitai](https://github.com/apps/coderabbitai)\n\nmentioned this [on Apr 28, 2025on Apr 28, 2025](https://github.com/arktypeio/arktype/issues/1110#event-2383450408)\n\n- [feat: option, product attach nextorders/food#388](https://github.com/nextorders/food/pull/388)\n\n\n[Sign up for free](https://github.com/signup?return_to=https://github.com/arktypeio/arktype/issues/1110)**to join this conversation on GitHub.** Already have an account? [Sign in to comment](https://github.com/login?return_to=https://github.com/arktypeio/arktype/issues/1110)\n\n## Metadata\n\n## Metadata\n\n### Assignees\n\nNo one assigned\n\n### Labels\n\n[confirmedThe maintainers of the repo would like to address this](https://github.com/arktypeio/arktype/issues?q=state%3Aopen%20label%3A%22confirmed%22) The maintainers of the repo would like to address this\n\n### Type\n\nNo type\n\n### Projects\n\n[arktypeio](https://github.com/orgs/arktypeio/projects/4)\n\nStatus\n\nDone (merged or closed)\n\nShow more project fields\n\n### Milestone\n\nNo milestone\n\n### Relationships\n\nNone yet\n\n### Development\n\nCode with agent mode\n\nSelect code repository\n\nNo branches or pull requests\n\n### Participants\n\n[![@ssalbdivad](https://avatars.githubusercontent.com/u/10645823?s=64&u=327eef4e0075cf2e169ae42f0c30ecb337878171&v=4)](https://github.com/ssalbdivad)[![@jacksteamdev](https://avatars.githubusercontent.com/u/23390212?s=64&v=4)](https://github.com/jacksteamdev)[![@semohr](https://avatars.githubusercontent.com/u/39738318?s=64&v=4)](https://github.com/semohr)\n\n## Issue actions\n\nYou can’t perform that action at this time.","metadata":{"octolytics-dimension-repository_nwo":"arktypeio/arktype","release":"51d2e33e3d1e4839c3ced5f8e35c7a47d3a60f32","og:image:alt":"Issue When using the string.date.parse type in combination with toJsonSchema I expected the type to be parsed to string, maybe even with the format keyword (see json-schema). Example import {type} ...","octolytics-url":"https://collector.github.com/github/collect","description":"Issue When using the string.date.parse type in combination with toJsonSchema I expected the type to be parsed to string, maybe even with the format keyword (see json-schema). Example import {type} from \"arktype\"; const date = type(\"strin...","twitter:image":"https://opengraph.githubassets.com/7e062031c0bda73cb7894296288e3a8e1385b074a2c34ee6553263b2f775f712/arktypeio/arktype/issues/1110","ogTitle":"Allow string format as metadata associated with JSON schema · Issue #1110 · arktypeio/arktype","go-import":"github.com/arktypeio/arktype git https://github.com/arktypeio/arktype.git","theme-color":"#1e2327","twitter:site":"@github","ogUrl":"https://github.com/arktypeio/arktype/issues/1110","route-action":"issue_layout","twitter:title":"Allow string format as metadata associated with JSON schema · Issue #1110 · arktypeio/arktype","request-id":"B38D:1BB5ED:4DAAEA:66354E:69C90B39","octolytics-dimension-repository_network_root_id":"193156479","expected-hostname":"github.com","og:type":"object","disable-turbo":"false","current-catalog-service-hash":"81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114","fetch-nonce":"v2:846d0763-879f-3781-32e8-5ee4164ab064","ogSiteName":"GitHub","apple-itunes-app":"app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/arktypeio/arktype/1110/issue_layout","octolytics-dimension-user_login":"arktypeio","google-site-verification":"Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I","og:description":"Issue When using the string.date.parse type in combination with toJsonSchema I expected the type to be parsed to string, maybe even with the format keyword (see json-schema). Example import {type} ...","analytics-location":"///voltron/issues_fragments/issue_layout","fb:app_id":"1401488693436528","github-keyboard-shortcuts":"repository,issues,copilot","ogDescription":"Issue When using the string.date.parse type in combination with toJsonSchema I expected the type to be parsed to string, maybe even with the format keyword (see json-schema). Example import {type} ...","ui-target":"full","color-scheme":"light dark","html-safe-nonce":"b316b3ada8fd476efa13e0fcbaffb3665875b196c454bfdddf8c9a49d27c22de","ogImage":"https://opengraph.githubassets.com/7e062031c0bda73cb7894296288e3a8e1385b074a2c34ee6553263b2f775f712/arktypeio/arktype/issues/1110","route-pattern":"/_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format)","visitor-payload":"eyJyZWZlcnJlciI6Imh0dHBzOi8vd3d3Lmdvb2dsZS5jb20vIiwicmVxdWVzdF9pZCI6IkIzOEQ6MUJCNUVEOjREQUFFQTo2NjM1NEU6NjlDOTBCMzkiLCJ2aXNpdG9yX2lkIjoiODQ2MjMwNzkxMzk5NzI5MDI5NyIsInJlZ2lvbl9lZGdlIjoiaWFkIiwicmVnaW9uX3JlbmRlciI6ImlhZCJ9","hostname":"github.com","og:image:width":"1200","og:image:height":"600","octolytics-dimension-repository_public":"true","browser-errors-url":"https://api.github.com/_private/browser/errors","route-controller":"voltron_issues_fragments","octolytics-dimension-repository_is_fork":"false","browser-stats-url":"https://api.github.com/_private/browser/stats","twitter:description":"Issue When using the string.date.parse type in combination with toJsonSchema I expected the type to be parsed to string, maybe even with the format keyword (see json-schema). Example import {type} ...","twitter:card":"summary_large_image","octolytics-dimension-repository_network_root_nwo":"arktypeio/arktype","octolytics-dimension-user_id":"52057462","og:image":"https://opengraph.githubassets.com/7e062031c0bda73cb7894296288e3a8e1385b074a2c34ee6553263b2f775f712/arktypeio/arktype/issues/1110","user-login":"","visitor-hmac":"4c22ced3f0cfc18caa0e88e12088766f0e86b91b3eba5134d386c3feed5f0082","title":"Allow string format as metadata associated with JSON schema · Issue #1110 · arktypeio/arktype","turbo-body-classes":"logged-out env-production page-responsive","language":"en","og:url":"https://github.com/arktypeio/arktype/issues/1110","hovercard-subject-tag":"issue:2497352681","viewport":"width=device-width","og:author:username":"semohr","og:site_name":"GitHub","og:title":"Allow string format as metadata associated with JSON schema · Issue #1110 · arktypeio/arktype","octolytics-dimension-repository_id":"193156479","turbo-cache-control":"no-preview","favicon":"https://github.githubassets.com/favicons/favicon.svg","scrapeId":"019d3953-d719-772d-b1bb-b1250336dc49","sourceURL":"https://github.com/arktypeio/arktype/issues/1110","url":"https://github.com/arktypeio/arktype/issues/1110","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","cacheState":"miss","indexId":"cabc9ac5-da3b-4b0f-8d58-799549641307","creditsUsed":1}},{"url":"https://arktype.io/docs/type-api","title":"Type API","description":"json. internal JSON representation ; toJsonSchema. generate a JSON Schema ; meta. metadata like custom descriptions and error messages. ✓ type. can be customized.","position":2,"markdown":"[🎉 Introducing ArkRegex 🎉](https://arktype.io/docs/blog/arkregex)\n\n# Type API\n\n| Name | Summary | Notes & Examples |\n| --- | --- | --- |\n| $ | [Scope](https://arktype.io/docs/type-api#Scope)
in which chained methods are parsed | |\n| infer | type of output this returns | 🥸 inference-only property that will be `undefined` at runtime
```
const parseNumber = type(\"string\").pipe(s => Number.parseInt(s))
type ParsedNumber = typeof parseNumber.infer // number
``` |\n| inferIn | type of input this allows | 🥸 inference-only property that will be `undefined` at runtime
```
const parseNumber = type(\"string\").pipe(s => Number.parseInt(s))
type UnparsedNumber = typeof parseNumber.inferIn // string
``` |\n| json | internal JSON representation | |\n| toJsonSchema | generate a JSON Schema | |\n| meta | metadata like custom descriptions and error messages | ✅ type
[can be customized](https://arktype.io/docs/configuration#custom)
for your project |\n| description | human-readable English description | ✅ works best for primitive values
```
const N = type(\"0 < number <= 100\")
console.log(N.description) // positive and at most 100
``` |\n| expression | syntax string similar to native TypeScript | ✅ works well for both primitives and structures
```
const Loc = type({ coords: [\"number\", \"number\"] })
console.log(Loc.expression) // { coords: [number, number] }
``` |\n| assert | validate and return transformed data or throw | ✅ sugar to avoid checking for
[type.errors](https://arktype.io/docs/type-api#type.errors)
if they are unrecoverable
```
const CriticalPayload = type({
superImportantValue: \"string\"
})
// throws TraversalError: superImportantValue must be a string (was missing)
const data = CriticalPayload.assert({ irrelevantValue: \"whoops\" })
console.log(data.superImportantValue) // valid output can be accessed directly
``` |\n| allows | check input without applying morphs | ✅ good for stuff like filtering that doesn't benefit from detailed errors
```
const Numeric = type(\"number | bigint\")
// [0, 2n]
const numerics = [0, \"one\", 2n].filter(Numeric.allows)
``` |\n| configure | add metadata to shallow references | ⚠️ does not affect error messages within properties of an object
```
const NotOdd = type(\"number % 2\").configure({ description: \"not odd\" })
// all constraints at the root are affected
const odd = NotOdd(3) // must be not odd (was 3)
const nonNumber = NotOdd(\"two\") // must be not odd (was \"two\")
const NotOddBox = type({
// we should have referenced notOdd or added meta here
notOdd: \"number % 2\",
// but instead chained from the root object
}).configure({ description: \"not odd\" })
// error message at path notOdd is not affected
const oddProp = NotOddBox({ notOdd: 3 }) // notOdd must be even (was 3)
// error message at root is affected, leading to a misleading description
const nonObject = NotOddBox(null) // must be not odd (was null)
``` |\n| describe | add description to shallow references | 🔗 equivalent to `.configure({ description })` (see
[configure](https://arktype.io/docs/type-api#configure)
)
⚠️ does not affect error messages within properties of an object
```
const AToZ = type(/^a.*z$/).describe(\"a string like 'a...z'\")
const good = AToZ(\"alcatraz\") // \"alcatraz\"
// ArkErrors: must be a string like 'a...z' (was \"albatross\")
const badPattern = AToZ(\"albatross\")
``` |\n| onUndeclaredKey | apply undeclared key behavior | • `\"ignore\"` (default) - allow and preserve extra properties
• `\"reject\"` \\- disallow extra properties
• `\"delete\"` \\- clone and remove extra properties from output |\n| onDeepUndeclaredKey | deeply apply undeclared key behavior | • `\"ignore\"` (default) - allow and preserve extra properties
• `\"reject\"` \\- disallow extra properties
• `\"delete\"` \\- clone and remove extra properties from output |\n| from | alias for
[assert](https://arktype.io/docs/type-api#assert)
with typed input | ```
const T = type({ foo: \"string\" });
// TypeScript: foo must be a string (was 5)
const data = T.from({ foo: 5 });
``` |\n| brand | add a compile-time brand to output | 🥸 inference-only function that does nothing runtime
```
const Palindrome = type(\"string\")
.narrow(s => s === [...s].reverse().join(\"\"))
.brand(\"palindrome\")
// Brand
const out = Palindrome.assert(\"racecar\")
``` |\n| array | an array of this | ```
// Type<{ rebmun: number }[]>
const T = type({ rebmun: \"number\" }).array();
``` |\n| optional | [optional definition](https://arktype.io/docs/objects#properties-optional) | ⚠️ unlike most other methods, this creates a definition rather than a Type (read why)
```
const Prop = type({ foo: \"number\" })
// Type<{ bar?: { foo: number } }>
const Obj = type({ bar: Prop.optional() })
``` |\n| default | [defaultable definition](https://arktype.io/docs/objects#properties-defaultable) | ✅ object defaults can be returned from a function
⚠️ throws if the default value is not allowed
⚠️ unlike most other methods, this creates a definition rather than a Type (read why)
```
// Type<{ count: Default }>
const State = type({ count: type.number.default(0) })
const Prop = type({ nested: \"boolean\" })
const ForObj = type({
key: Prop.default(() => ({ nested: false }))
})
``` |\n| filter | apply a predicate function to input | ⚠️ the behavior of
[narrow](https://arktype.io/docs/type-api#narrow)
, this method's output counterpart, is usually more desirable
✅ most useful for morphs with input types that are re-used externally
🥸
[Type predicates](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
can be used as casts
```
const stringifyUser = type({ name: \"string\" }).pipe(user => JSON.stringify(user))
const stringifySafe = stringifyUser.filter(user => user.name !== \"Bobby Tables\")
// Type<(In: `${string}Z`) => To>
const WithPredicate = type(\"string.date.parse\").filter((s): s is `${string}Z` =>
s.endsWith(\"Z\")
)
``` |\n| narrow | apply a predicate function to output | ✅ go-to fallback for validation not composable via built-in types and operators
✅ runs after all other validators and morphs, if present
🥸
[Type predicates](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
can be used as casts
```
const Palindrome = type(\"string\").narrow(s => s === [...s].reverse().join(\"\"))
const PalindromicEmail = type(\"string.date.parse\").narrow((date, ctx) =>
\t\tdate.getFullYear() === 2025 || ctx.mustBe(\"the current year\")
)
// Type<`${string}.tsx`>
const WithPredicate = type(\"string\").narrow((s): s is `${string}.tsx` => /\\.tsx?$/.test(s))
``` |\n| pipe | pipe output through arbitrary transformations or other Types | ```
const User = type({ name: \"string\" })
// parse a string and validate that the result as a user
const parseUser = type(\"string\").pipe(s => JSON.parse(s), user)
``` |\n| to | parse a definition as an output validator | 🔗 `to({ name: \"string\" })` is equivalent to `.pipe(type({ name: \"string\" }))`
```
// parse a string and validate that the result as a user
const parseUser = type(\"string\").pipe(s => JSON.parse(s)).to({ name: \"string\" })
``` |\n| select | query internal node references | filters and returns the Type's internal representation from `@ark/schema`
```
// [\"blue\", \"red\"]
const values = type(\"'red' | 'blue'\").select(\"unit\").map(u => u.unit)
``` |\n| as | cast the way this is inferred | 🥸 inference-only function that does nothing runtime
```
// Type<`LEEEEEEEE${string}ROY`>
const Leeroy = type(/^LE{8,}ROY$/).as<`LEEEEEEEE${string}ROY`>()
``` |\n| and | intersect the parsed Type, throwing if the result is unsatisfiable | ```
// Type<{ foo: number; bar: string }>
const T = type({ foo: \"number\" }).and({ bar: \"string\" })
// ParseError: Intersection at foo of number and string results in an unsatisfiable type
const Bad = type({ foo: \"number\" }).and({ foo: \"string\" })
``` |\n| or | union with the parsed Type | ⚠️ a union that could apply different morphs to the same data is a ParseError (
[docs](https://arktype.io/docs/expressions#union-morphs)
)
```
// Type
const T = type(\"string\").or({ box: \"string\" })
``` |\n| intersect | intersect the parsed Type, returning an introspectable
[Disjoint](https://arktype.io/docs/type-api#Disjoint)
if the result is unsatisfiable | ```
// Type<{ foo: number; bar: string }>
const T = type({ foo: \"number\" }).intersect({ bar: \"string\" })
const Bad = type(\"number > 10\").intersect(\"number < 5\")
// logs \"Intersection of > 10 and < 5 results in an unsatisfiable type\"
if (Bad instanceof Disjoint) console.log(`${bad.summary}`)
``` |\n| equals | check if the parsed Type's constraints are identical | ✅ equal types have identical input and output constraints and transforms
✅ ignores associated
[meta](https://arktype.io/docs/type-api#meta)
, which does not affect the set of allowed values
```
const DivisibleBy6 = type.number.divisibleBy(6).moreThan(0)
// false (left side must also be positive)
DivisibleBy6.equals(\"number % 6\")
// false (right side has an additional <100 constraint)
console.log(DivisibleBy6.equals(\"0 < (number % 6) < 100\"))
const ThirdTry = type(\"(number % 2) > 0\").divisibleBy(3)
// true (types are normalized and reduced)
console.log(DivisibleBy6.equals(ThirdTry))
``` |\n| ifEquals | narrow this based on an
[equals](https://arktype.io/docs/type-api#equals)
check | ✅ ignores associated
[meta](https://arktype.io/docs/type-api#meta)
, which does not affect the set of allowed values
```
const N = type.raw(`${Math.random()}`)
// Type<0.5> | undefined
const Ez = N.ifEquals(\"0.5\")
``` |\n| extends | check if this is a subtype of the parsed Type | ✅ a subtype must include all constraints from the base type
✅ unlike
[equals](https://arktype.io/docs/type-api#equals)
, additional constraints may be present
✅ ignores associated
[meta](https://arktype.io/docs/type-api#meta)
, which does not affect the set of allowed values
```
type.string.extends(\"unknown\") // true
type.string.extends(/^a.*z$/) // false
``` |\n| ifExtends | narrow this based on an
[extends](https://arktype.io/docs/type-api#extends)
check | ✅ ignores associated
[meta](https://arktype.io/docs/type-api#meta)
, which does not affect the set of allowed values
```
const N = type(Math.random() > 0.5 ? \"true\" : \"0\") // Type<0 | true>
const Ez = N.ifExtends(\"boolean\") // Type | undefined
``` |\n| overlaps | check if a value could satisfy this and the parsed Type | ⚠️ will return true unless a
[Disjoint](https://arktype.io/docs/type-api#Disjoint)
can be proven
```
type.string.overlaps(\"string | number\") // true (e.g. \"foo\")
type(\"string | number\").overlaps(\"1\") // true (1)
type(\"number > 0\").overlaps(\"number < 0\") // false (no values exist)
const NoAt = type(\"string\").narrow(s => !s.includes(\"@\"))
NoAt.overlaps(\"string.email\") // true (no values exist, but not provable)
``` |\n| extract | extract branches
[extend](https://arktype.io/docs/type-api#extend)
ing the parsed Type | ```
// Type
const T = type(\"boolean | 0 | 'one' | 2 | bigint\").extract(\"number | 0n | true\")
``` |\n| exclude | exclude branches
[extend](https://arktype.io/docs/type-api#extend)
ing the parsed Type | ```
// Type
const T = type(\"boolean | 0 | 'one' | 2 | bigint\").exclude(\"number | 0n | true\")
``` |\n\nThe methods below are available on every `Type` instance. For validation and traversal methods (`assert`, `allows`, direct invocation), see the [Traversal API](https://arktype.io/docs/traversal-api). For composition methods (`pipe`, `to`, `narrow`, `filter`, `and`, `or`), see [Expressions](https://arktype.io/docs/expressions). For object-specific methods (`pick`, `omit`, `required`, `partial`, `merge`, `keyof`, `get`, `readonly`, `map`, `props`), see [Objects](https://arktype.io/docs/objects).\n\n### [from](https://arktype.io/docs/type-api\\#from)\n\n`from` is a typed-input variant of `assert`. It accepts an input matching `inferIn` and returns `inferOut`, providing type safety on both sides. Like `assert`, it throws a `TraversalError` on invalid input:\n\n```\nconst StringToNumber = type(\"string.numeric.parse\")\n\n// TypeScript knows the input must be a string\nconst result = StringToNumber.from(\"42\") // 42\n\nStringToNumber.from(42)Argument of type 'number' is not assignable to parameter of type 'string'.\n```\n\n### [in / out](https://arktype.io/docs/type-api\\#in--out)\n\nThe `in` and `out` getters extract the input or output Type from a morphed Type, stripping transformations:\n\n```\nconst ParsedUser = type({\n\tname: \"string\",\n\tage: \"string.numeric.parse\"\n})\n\n// Type<{ name: string; age: string }>\nconst UserInput = ParsedUser.in\n\n// Type<{ name: string; age: number }>\nconst UserOutput = ParsedUser.out\n```\n\n### [extends](https://arktype.io/docs/type-api\\#extends)\n\nCheck if a Type is a subtype of another:\n\n```\nconst T = type(\"string\")\n\nT.extends(\"unknown\") // true\nT.extends(\"number\") // false\n\n// ifExtends returns the Type itself if true, undefined otherwise\nconst result = T.ifExtends(\"string | number\") // Type | undefined\n```\n\n### [equals](https://arktype.io/docs/type-api\\#equals)\n\nCheck if two Types are structurally identical:\n\n```\nconst A = type({ name: \"string\" })\nconst B = type({ name: \"string\" })\nconst C = type({ name: \"number\" })\n\nA.equals(B) // true\nA.equals(C) // false\n\n// ifEquals returns the Type if equal, undefined otherwise\nconst result = A.ifEquals(B) // Type<{ name: string }> | undefined\n```\n\n### [overlaps](https://arktype.io/docs/type-api\\#overlaps)\n\nCheck if any value could satisfy both Types:\n\n```\nconst A = type(\"string | number\")\nconst B = type(\"number | boolean\")\nconst C = type(\"string\")\n\nA.overlaps(B) // true (number satisfies both)\nC.overlaps(type(\"number\")) // false\n```\n\n### [extract / exclude](https://arktype.io/docs/type-api\\#extract--exclude)\n\nExtract or exclude union branches based on a Type:\n\n```\nconst T = type(\"string | number | boolean\")\n\n// Type\nconst Extracted = T.extract(\"string | boolean\")\n\n// Type\nconst Excluded = T.exclude(\"string | boolean\")\n```\n\n### [distribute](https://arktype.io/docs/type-api\\#distribute)\n\nMap and optionally reduce over union branches:\n\n```\nconst T = type(\"string | number | bigint\")\n\n// [\"bigint\", \"number\", \"string\"]\nconst expressions = T.distribute(branch => branch.expression)\n\n// with a reducer\nconst count = T.distribute(\n\tbranch => branch,\n\tbranches => branches.length\n) // 3\n```\n\n### [toJsonSchema](https://arktype.io/docs/type-api\\#tojsonschema)\n\nEach `Type` instance exposes a `toJsonSchema()` method that can be used to generate a corresponding JSON Schema.\n\n```\nconst User = type({\n\tname: \"string\",\n\temail: \"string.email\",\n\t\"age?\": \"number >= 18\"\n})\n\nconst schema = User.toJsonSchema()\n\nconst result = {\n\t$schema: \"https://json-schema.org/draft/2020-12/schema\",\n\ttype: \"object\",\n\tproperties: {\n\t\tname: { type: \"string\" },\n\t\temail: {\n\t\t\ttype: \"string\",\n\t\t\tformat: \"email\",\n\t\t\tpattern: \"^[\\w%+.-]+@[\\d.A-Za-z-]+\\.[A-Za-z]{2,}$\"\n\t\t},\n\t\tage: { type: \"number\", minimum: 18 }\n\t},\n\trequired: [\"email\", \"name\"]\n}\n```\n\nOptions can be passed to change the behavior including how incompatibilities are handled. See [the associated config docs](https://arktype.io/docs/configuration#tojsonschema) for more details.\n\n[prototypes\\\\\n\\\\\nPrevious Page](https://arktype.io/docs/configuration#prototypes) [from\\\\\n\\\\\nNext Page](https://arktype.io/docs/type-api#from)","metadata":{"og:image:height":"600","ogTitle":"ArkType Docs","ogDescription":"TypeScript's 1:1 validator, optimized from editor to runtime","ogSiteName":"ArkType","viewport":"width=device-width, initial-scale=1","keywords":"ArkType,TypeScript,JavaScript,runtime validation,schema,type-safe,validator,syntax","og:url":"https://arktype.io/","twitter:card":"summary_large_image","og:type":"website","ogUrl":"https://arktype.io/","twitter:description":"TypeScript's 1:1 validator, optimized from editor to runtime","og:title":"ArkType Docs","og:site_name":"ArkType","twitter:image":"https://arktype.io/image/ogDocs.png","language":"en","next-size-adjust":"","twitter:title":"ArkType Docs","twitter:image:width":"1200","og:image":"https://arktype.io/image/ogDocs.png","ogImage":"https://arktype.io/image/ogDocs.png","og:description":"TypeScript's 1:1 validator, optimized from editor to runtime","twitter:image:height":"600","og:image:width":"1200","title":"Type API","favicon":"https://arktype.io/image/favicon.svg","scrapeId":"019d3953-d719-772d-b1bb-b5bcabc1c87f","sourceURL":"https://arktype.io/docs/type-api","url":"https://arktype.io/docs/type-api","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","cacheState":"miss","indexId":"d9a70ba9-7c12-416c-a93a-62a950b8046d","creditsUsed":1}},{"url":"https://arktype.io/docs/blog/2.1","title":"Announcing ArkType 2.1","description":"A pattern matching API that allows you to define cases using expressive type syntax. The result is a highly optimized matcher that uses set theory to ...","position":3,"markdown":"[🎉 Introducing ArkRegex 🎉](https://arktype.io/docs/blog/arkregex)\n\n[Blog](https://arktype.io/docs/blog)\n\n# Announcing ArkType 2.1 Optimized pattern matching from type syntax\n\nAs of today, 2.1.0 is generally available 🎉\n\nThe biggest feature is `match`, a pattern matching API that allows you to define cases using expressive type syntax. The result is a highly optimized matcher that uses set theory to automatically skip unmatched branches.\n\nWe could not be more excited to share this not just as the first syntactic matcher in JS, but as the first ArkType feature to showcase the potential of runtime types to do more than just validation.\n\nLanguages with introspectable types offer incredibly powerful features that have always felt out of reach in JS- until now.\n\n```\nconst toJsonArkType = match({\n\t\"string | number | boolean | null\": v => v,\n\tbigint: b => `${b}n`,\n\tobject: o => {\n\t\tfor (const k in o) {\n\t\t\to[k] = toJsonArkType(o[k])\n\t\t}\n\t\treturn o\n\t},\n\tdefault: \"assert\"\n})\n\nconst toJsonTsPattern = (value: unknown) =>\n\ttsPatternMatch(value)\n\t\t.with(P.union(P.string, P.number, P.boolean, null), v => v)\n\t\t.with(P.bigint, v => `${v}n`)\n\t\t.with({}, o => {\n\t\t\tfor (const k in o) {\n\t\t\t\to[k] = toJsonTsPattern(o[k])\n\t\t\t}\n\t\t\treturn o\n\t\t})\n\t\t.otherwise(() => {\n\t\t\tthrow new Error(\"value is not valid JSON\")\n\t\t})\n\n// \"foo\" (9 nanoseconds)\ntoJsonArkType(\"foo\")\n// \"foo\" (765 nanoseconds)\ntoJsonTsPattern(\"foo\")\n\n// \"5n\" (33 nanoseconds)\ntoJsonArkType(5n)\n// \"5n\" (924 nanoseconds)\ntoJsonTsPattern(5n)\n\n// { nestedValue: \"5n\" } (44 nanoseconds)\ntoJsonArkType({ nestedValue: 5n })\n// { nestedValue: \"5n\" } (2080 nanoseconds)\ntoJsonTsPattern({ nestedValue: 5n })\n```\n\nWe're actually huge fans of [Gabriel Vergnaud](https://github.com/gvergnaud) and [ts-pattern](https://github.com/gvergnaud/ts-pattern), which has a great API and totally reasonable performance. We've referenced it for comparison to showcase the unique expressiveness and optimization runtime types unlock.\n\nBelow are the full notes for the 2.1.0 release. We can't wait to hear what you think! 🚀\n\n### [`match`](https://arktype.io/docs/blog/2.1\\#match)\n\nThe `match` function provides a powerful way to handle different types of input and return corresponding outputs based on the input type, like a type-safe `switch` statement.\n\n#### [Case Record API](https://arktype.io/docs/blog/2.1\\#case-record-api)\n\nThe simplest way to define a matcher is with ArkType definition strings as keys with corresponding handlers as values:\n\n```\nimport { match } from \"arktype\"\n\nconst sizeOf = match({\n\t\"string | Array\": v => v.length,\n\tnumber: v => v,\n\tbigint: v => v,\n\tdefault: \"assert\"\n})\n\n// a match definition is complete once a `default` has been specified,\n// either as a case or via the .default() method\n\nsizeOf(\"abc\") // 3\nsizeOf([1, 2, 3, 4]) // 4\nsizeOf(5n) // 5n\n// ArkErrors: must be an object, a string, a number or a bigint (was boolean)\nsizeOf(true)\n```\n\nIn this example, `sizeOf` is a matcher that takes a string, array, number, or bigint as input. It returns the length of strings and arrays, and the value of numbers and bigints.\n\n`default` accepts one of 4 values:\n\n- `\"assert\"`: accept `unknown`, throw if none of the cases match\n- `\"never\"`: accept an input based on inferred cases, throw if none match\n- `\"reject\"`: accept `unknown`, return `ArkErrors` if none of the cases match\n- `(data: In) => unknown`: handle data not matching other cases directly\n\nCases will be checked in the order they are specified, either as object literal keys or via chained methods.\n\n#### [Fluent API](https://arktype.io/docs/blog/2.1\\#fluent-api)\n\nThe `match` function also provides a fluent API. This can be convenient for non-string-embeddable definitions:\n\n```\n// the Case Record and Fluent APIs can be easily combined\nconst sizeOf = match({\n\tstring: v => v.length,\n\tnumber: v => v,\n\tbigint: v => v\n})\n\t// match any object with a numeric length property and extract it\n\t.case({ length: \"number\" }, o => o.length)\n\t// return 0 for all other data\n\t.default(() => 0)\n\nsizeOf(\"abc\") // 3\nsizeOf({ name: \"David\", length: 5 }) // 5\nsizeOf(null) // 0\n```\n\n#### [Narrowing input with `in`, property matching with `at`](https://arktype.io/docs/blog/2.1\\#narrowing-input-with-in-property-matching-with-at)\n\n```\ntype Data =\n\t| {\n\t\t\tid: 1\n\t\t\toneValue: number\n\t }\n\t| {\n\t\t\tid: 2\n\t\t\ttwoValue: string\n\t }\n\nconst discriminateValue = match\n\t// .in allows you to specify the input TypeScript allows for your matcher\n\t.in()\n\t// .at allows you to specify a key at which your input will be matched\n\t.at(\"id\")\n\t.match({\n\t\t1: o => `${o.oneValue}!`,\n\t\t2: o => o.twoValue.length,\n\t\tdefault: \"assert\"\n\t})\n\ndiscriminateValue({ id: 1, oneValue: 1 }) // \"1!\"\ndiscriminateValue({ id: 2, twoValue: \"two\" }) // 3\ndiscriminateValue({ oneValue: 3 })TypeScript: Property 'id' is missing in type '{ oneValue: number; }' but required in type '{ id: 1; oneValue: number; }'.\n```\n\nSpecial thanks to [@thetayloredman](https://github.com/thetayloredman) who did a mind-blowingly good job helping us iterate toward the current type-level pattern-matching implementation🙇\n\n### [Built-in keywords can now be globally configured](https://arktype.io/docs/blog/2.1\\#built-in-keywords-can-now-be-globally-configured)\n\nThis can be very helpful for customizing error messages without needing to create your own aliases or wrappers.\n\nconfig.ts\n\n```\nimport { configure } from \"arktype/config\"\n\nconfigure({\n\tkeywords: {\n\t\tstring: \"shorthand description\",\n\t\t\"string.email\": {\n\t\t\tactual: () => \"definitely fake\"\n\t\t}\n\t}\n})\n```\n\napp.ts\n\n```\nimport \"./config.ts\"\nimport { type } from \"arktype\"\n\nconst User = type({\n\tname: \"string\",\n\temail: \"string.email\"\n})\n\nconst out = User({\n\t// ArkErrors: name must be shorthand description (was a number)\n\tname: 5,\n\t// ArkErrors: email must be an email address (was definitely fake)\n\temail: \"449 Canal St\"\n})\n```\n\nThe options you can provide here are identical to those used to [configure a Type directly](https://arktype.io/docs/expressions#meta), and can also be [extended at a type-level to include custom metadata](https://arktype.io/docs/configuration#metadata).\n\n### [Tuple and args expressions for `.to`](https://arktype.io/docs/blog/2.1\\#tuple-and-args-expressions-for-to)\n\nIf a morph returns an `ArkErrors` instance, validation will fail with that result instead of it being treated as a value. This is especially useful for using other Types as morphs to validate output or chain transformations.\n\nTo make this easier, there's a special `to` operator that can pipe to a parsed definition without having to wrap it in `type` to make it a function.\n\nThis was added before 2.0, but now it comes with a corresponding operator (`|>`) so that it can be expressed via a tuple or args like most other expressions:\n\n```\nconst FluentStillWorks = type(\"string.numeric.parse\").to(\"number % 2\")\n\nconst NowSoDoesTuple = type({\n\tsomeKey: [\"string.numeric.parse\", \"|>\", \"number % 2\"]\n})\n\nconst AndSpreadArgs = type(\"string.numeric.parse\", \"|>\", \"number % 2\")\n```\n\n### [Error configurations now accept a string directly](https://arktype.io/docs/blog/2.1\\#error-configurations-now-accept-a-string-directly)\n\n```\nconst CustomOne = type(\"1\", \"@\", {\n\t// previously only a function returning a string was allowed here\n\tmessage: \"Yikes.\"\n})\n\n// ArkErrors: Yikes.\nCustomOne(2)\n```\n\nKeep in mind, [as mentioned in the docs](https://arktype.io/docs/configuration#errors), error configs like `message` can clobber more granular config options like `expected` and `actual` and cannot be included in composite errors e.g. for a union.\n\nThough generally, returning a string based on context is the best option, in situations where you always want the same static message, it's now easier to get that!\n\n### [Type.toString() now wraps its syntactic representation in `Type<..>`](https://arktype.io/docs/blog/2.1\\#typetostring-now-wraps-its-syntactic-representation-in-type)\n\nPreviously, `Type.toString()` just returned `Type.expression`. However, in contexts where the source of a message isn't always a `Type`, it could be confusing:\n\n```\n// < 2.1.0: \"(was string)\"\n// >= 2.1.0: \"(was Type)\"\nconsole.log(`(was ${type.string})`)\n```\n\nHopefully if you interpolate a Type, you'll be less confused by the result from now on!\n\n### [Improve how Type instances are inferred when wrapped in external generics](https://arktype.io/docs/blog/2.1\\#improve-how-type-instances-are-inferred-when-wrapped-in-external-generics)\n\nPreviously, we used `NoInfer` in some Type method returns. After migrating those to inlined conditionals, we get the same benefit and external inference for cases like this is more reliable:\n\n```\nfunction fn<\n\tT extends {\n\t\tschema: StandardSchemaV1\n\t}\n>(_: T) {\n\treturn {} as StandardSchemaV1.InferOutput\n}\n\n// was inferred as unknown (now correctly { name: string })\nconst arkRes = fn({\n\tschema: type({\n\t\tname: \"string\"\n\t})\n})\n```\n\n### [Fix an issue causing some discriminated unions to incorrectly reject default cases](https://arktype.io/docs/blog/2.1\\#fix-an-issue-causing-some-discriminated-unions-to-incorrectly-reject-default-cases)\n\n```\nconst Discriminated = type({\n\tid: \"0\",\n\tk1: \"number\"\n})\n\t.or({ id: \"1\", k1: \"number\" })\n\t.or({\n\t\tname: \"string\"\n\t})\n\n// previously, this was rejected as requiring a \"k1\" key\n// will now hit the case discriminated for id: 1,\n// but still correctly be allowed via the { name: string } branch\nDiscriminated({ name: \"foo\", id: 1 })\n```\n\n[ArkRegex Intro\\\\\n\\\\\nPrevious Page](https://arktype.io/docs/blog/arkregex) [2.0 Announcement\\\\\n\\\\\nNext Page](https://arktype.io/docs/blog/2.0)\n\n### On this page\n\n[`match`](https://arktype.io/docs/blog/2.1#match) [Case Record API](https://arktype.io/docs/blog/2.1#case-record-api) [Fluent API](https://arktype.io/docs/blog/2.1#fluent-api) [Narrowing input with `in`, property matching with `at`](https://arktype.io/docs/blog/2.1#narrowing-input-with-in-property-matching-with-at) [Built-in keywords can now be globally configured](https://arktype.io/docs/blog/2.1#built-in-keywords-can-now-be-globally-configured) [Tuple and args expressions for `.to`](https://arktype.io/docs/blog/2.1#tuple-and-args-expressions-for-to) [Error configurations now accept a string directly](https://arktype.io/docs/blog/2.1#error-configurations-now-accept-a-string-directly) [Type.toString() now wraps its syntactic representation in `Type<..>`](https://arktype.io/docs/blog/2.1#typetostring-now-wraps-its-syntactic-representation-in-type) [Improve how Type instances are inferred when wrapped in external generics](https://arktype.io/docs/blog/2.1#improve-how-type-instances-are-inferred-when-wrapped-in-external-generics) [Fix an issue causing some discriminated unions to incorrectly reject default cases](https://arktype.io/docs/blog/2.1#fix-an-issue-causing-some-discriminated-unions-to-incorrectly-reject-default-cases)","metadata":{"ogTitle":"ArkType Docs","og:title":"ArkType Docs","ogDescription":"TypeScript's 1:1 validator, optimized from editor to runtime","og:description":"TypeScript's 1:1 validator, optimized from editor to runtime","twitter:title":"ArkType Docs","twitter:image":"https://arktype.io/image/ogDocs.png","ogUrl":"https://arktype.io/","og:image":"https://arktype.io/image/ogDocs.png","twitter:card":"summary_large_image","og:type":"website","twitter:image:width":"1200","twitter:description":"TypeScript's 1:1 validator, optimized from editor to runtime","keywords":"ArkType,TypeScript,JavaScript,runtime validation,schema,type-safe,validator,syntax","language":"en","ogImage":"https://arktype.io/image/ogDocs.png","title":"Announcing ArkType 2.1","ogSiteName":"ArkType","next-size-adjust":"","description":"Optimized pattern matching from type syntax","og:url":"https://arktype.io/","viewport":"width=device-width, initial-scale=1","og:site_name":"ArkType","og:image:height":"600","og:image:width":"1200","twitter:image:height":"600","favicon":"https://arktype.io/image/favicon.svg","scrapeId":"019d3953-d719-772d-b1bb-bb000e8c2bc7","sourceURL":"https://arktype.io/docs/blog/2.1","url":"https://arktype.io/docs/blog/2.1","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","cacheState":"miss","indexId":"62277ba5-56dc-4205-8155-93f284f857d9","creditsUsed":1}},{"url":"https://www.renoun.dev/guides/arktype","title":"ArkType Guide - renoun - Query and Render Your Codebase","description":"In this guide, we'll walk through how to use ArkType to add schema validation to your file system. ArkType provides a concise, TypeScript ...","position":4,"markdown":"# ArkType Guide\n\nLearn how to add schema validation to your file system using ArkType in renoun.\n\n```\nimport { Directory } from 'renoun'\nimport { type } from 'arktype'\n\nconst frontmatterSchema = type({\n title: 'string',\n date: 'Date',\n 'summary?': 'string',\n 'tags?': 'string[]',\n})\n\nconst posts = new Directory({\n path: 'posts',\n filter: '*.mdx',\n schema: {\n mdx: { frontmatter: frontmatterSchema },\n },\n loader: {\n mdx: (path) => import(`./posts/${path}.mdx`),\n },\n})\n```\n\n## [Introduction](https://www.renoun.dev/guides/arktype\\#introduction)\n\nIn this guide, we’ll walk through how to use [ArkType](https://arktype.io/) to add schema validation to your file system. ArkType provides a concise, TypeScript-aligned syntax with fast runtime validation and a great editor experience.\n\n### [Before You Begin](https://www.renoun.dev/guides/arktype\\#before-you-begin)\n\nBefore you start, make sure you have a basic understanding of how the [File System API](https://www.renoun.dev/utilities/file-system) works in renoun. We’ll also be using MDX files in this guide, so make sure you’re familiar with the [MDX Guide](https://www.renoun.dev/guides/mdx) as well.\n\n## [Using ArkType](https://www.renoun.dev/guides/arktype\\#using-ark-type)\n\nArkType is a TypeScript-first validator with a 1:1 type syntax and strong developer ergonomics. Let’s look at how you can use ArkType to add schema validation to your file system in renoun.\n\n### [Install](https://www.renoun.dev/guides/arktype\\#install)\n\nFirst, install `arktype` using your package manager:\n\nnpmpnpmyarnbun\n\n```\nnpm install arktype\n```\n\n```\npnpm add arktype\n```\n\n```\nyarn add arktype\n```\n\n```\nbun add arktype\n```\n\n### [Define Schema](https://www.renoun.dev/guides/arktype\\#define-schema)\n\nNow, we’ll create a schema using `arktype` for the frontmatter of an MDX file:\n\n```\nimport { type } from 'arktype'\n\nconst frontmatterSchema = type({\n title: 'string',\n date: 'Date',\n 'summary?': 'string',\n 'tags?': 'string[]',\n})\n```\n\n### [Apply to a Directory](https://www.renoun.dev/guides/arktype\\#apply-to-a-directory)\n\nWe can now apply the ArkType `frontmatterSchema` to your `Directory` using the `schema` option:\n\n```\nimport { Directory } from 'renoun'\nimport { type } from 'arktype'\n\nconst frontmatterSchema = type({\n title: 'string',\n date: 'Date',\n 'summary?': 'string',\n 'tags?': 'string[]',\n})\n\nconst posts = new Directory({\n path: 'posts',\n filter: '*.mdx',\n schema: {\n mdx: { frontmatter: frontmatterSchema },\n },\n loader: {\n mdx: (path) => import(`./posts/${path}.mdx`),\n },\n})\n```\n\nNow, the `frontmatter` export in your MDX files will be validated against the `frontmatterSchema` we defined using ArkType. If the data does not match the schema, an error will be thrown.\n\n## [Beyond Front Matter](https://www.renoun.dev/guides/arktype\\#beyond-front-matter)\n\nWhile the example in this guide focused on validating frontmatter in MDX files, the same approach can be applied to validate any kind of export within a file. Whether you need to enforce a specific structure for other metadata, content fields, or custom data exports, ArkType provides the flexibility to define schemas that fit your file system requirements.\n\n## [Conclusion](https://www.renoun.dev/guides/arktype\\#conclusion)\n\nBy using ArkType, you can add fast and ergonomic schema validation to your file system. This ensures that your data is always in the expected format, making your application more robust and maintainable. For more information, refer to the [ArkType documentation](https://arktype.io/).\n\nLast updated01/03/26\n\n:#A492EA}\\\\\"\\]\\],\\[\\]\\],\\\\\"n\\\\\":\\\\\"$undefined\\\\\"}\\]\\]}\\],\\\\\")\\\\\"\\],null\\]}\\]\\\\n\"\\])","metadata":{"ogImage":"https://renoun.dev/opengraph-image.png?cf6104e0736a9017","twitter:title":"renoun - Query and Render Your Codebase","twitter:description":"Turn your JavaScript, TypeScript, Markdown, and MDX into reusable structured data for blogs, docs, and presentations.","og:image:width":"1800","twitter:image:type":"image/png","og:image":"https://renoun.dev/opengraph-image.png?cf6104e0736a9017","twitter:card":"summary_large_image","viewport":"width=device-width, initial-scale=1","twitter:image:height":"1013","ogDescription":"Turn your JavaScript, TypeScript, Markdown, and MDX into reusable structured data for blogs, docs, and presentations.","language":"en","og:title":"renoun - Query and Render Your Codebase","ogTitle":"renoun - Query and Render Your Codebase","next-size-adjust":"","description":"Turn your JavaScript, TypeScript, Markdown, and MDX into reusable structured data for blogs, docs, and presentations.","og:image:type":"image/png","title":"renoun - Query and Render Your Codebase","og:image:height":"1013","twitter:image:width":"1800","twitter:image":"https://renoun.dev/opengraph-image.png?cf6104e0736a9017","og:description":"Turn your JavaScript, TypeScript, Markdown, and MDX into reusable structured data for blogs, docs, and presentations.","favicon":"https://www.renoun.dev/favicon-light.svg","scrapeId":"019d3953-d719-772d-b1bb-bfa0f8618bdd","sourceURL":"https://www.renoun.dev/guides/arktype","url":"https://www.renoun.dev/guides/arktype","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","cacheState":"miss","indexId":"cb0fb516-0f2a-4f70-b895-3cc2ce89f236","creditsUsed":1}},{"url":"https://arktype.io/docs/configuration","title":"Configuration","description":"Some ArkType features don't have JSON Schema equivalents. By default, toJsonSchema() will throw in these cases. This behavior can be configured granularly to ...","position":5,"markdown":"[🎉 Introducing ArkRegex 🎉](https://arktype.io/docs/blog/arkregex)\n\n# Configuration\n\nA great out-of-the-box experience is a core goal of ArkType, including safe defaults and helpful messages for complex errors.\n\nHowever, it's equally important that when you need different behavior, you can easily configure it with the right granularity.\n\n### [Levels](https://arktype.io/docs/configuration\\#levels)\n\n| Level | Applies To | Example |\n| --- | --- | --- |\n| **default** | built-in defaults for all Types | |\n| **global** | all Types parsed after the config is applied | config.ts
```
import { configure } from \"arktype/config\"
// use the \"arktype/config\" entrypoint
configure({ numberAllowsNaN: true })
```
app.ts
```
import \"./config.ts\"
// import your config file before arktype
import { type } from \"arktype\"
type.number.allows(Number.NaN) // true
``` |\n| **scope** | all Types parsed in the configured Scope | ```
const myScope = scope(
\t{ User: { age: \"number < 100\" } },
\t{
\t\tmax: {
\t\t\tactual: () => \"unacceptably large\"
\t\t}
\t}
)
const types = myScope.export()
// ArkErrors: age must be less than 100 (was unacceptably large)
types.User({ name: \"Alice\", age: 101 })
const parsedAfter = myScope.type({
\tage: \"number <= 100\"
})
// ArkErrors: age must be at most 100 (was unacceptably large)
parsedAfter({ age: 101 })
``` |\n| **type** | all Types shallowly referenced by the configured Type | ```
// avoid logging \"was xxx\" for password
const Password = type(\"string >= 8\").configure({ actual: () => \"\" })
const User = type({
\temail: \"string.email\",
\tpassword: Password
})
// ArkErrors: password must be at least length 8
const out = User({
\temail: \"david@arktype.io\",
\tpassword: \"ez123\"
})
``` |\n\nSome options only apply at specific levels, as reflected in the corresponding input types.\n\nUse the \\`\"arktype/config\"\\` entrypoint in a separate file for global config!\n\nIf you need your config to apply to built-in keywords (important for options\nlike `jitless`, `numberAllowsNaN`, `dateAllowsInvalid`), you should import and\n`configure` from `\"arktype/config\"` before importing anything from\n`\"arktype\"`.\n\nOtherwise, keywords will have already been parsed by the time your config applies!\n\n### [Errors](https://arktype.io/docs/configuration\\#errors)\n\nTo allow custom errors to be integrated seamlessly with built-in logic for composite errors (i.e. `union` and `intersection`), ArkType supports a set of composable options:\n\n| optional | description | example |\n| --- | --- | --- |\n| **description** | ✅ a summary of the constraint that could complete the phrase \"must be \\_\\_\\_\"
🥇 reused by other metadata and should be your first go-to for customizing a message | ```
const Password = type.string.atLeastLength(8).describe(\"a valid password\")
// ArkErrors: must be a valid password
const out = Password(\"ez123\")
``` |\n| **expected** | ✅ a function accepting the error context and returning a string of the format \"must be \\_\\_\\_\"
✅ specific to errors and takes precedence over `description` in those cases | ```
const Password = type.string.atLeastLength(8).configure({
\texpected: ctx =>
\t\tctx.code === \"minLength\" ? `${ctx.rule} characters or better` : \"way better\"
})
// ArkErrors: must be 8 characters or better (was 5)
const out1 = Password(\"ez123\").toString()
// ArkErrors: must be way better (was a number)
const out2 = Password(12345678).toString()
``` |\n| **actual** | ✅ a function accepting the data that caused the error and returning a string of the format \"(was \\_\\_\\_)\"
✅ if an empty string is returned, the actual portion of the message will be omitted | ```
const Password = type(\"string >= 8\").configure({ actual: () => \"\" })
// ArkErrors: must be at least length 8
const out = Password(\"ez123\")
``` |\n| **problem** | ✅ a function accepting the results of `expected` and `actual` in addition to other context and returning a complete description of the problem like \"must be a string (was a number)\"
❌ may not apply to composite errors like unions | ```
const Password = type(\"string >= 8\").configure({
\tproblem: ctx => `${ctx.actual} isn't ${ctx.expected}`
})
// ArkErrors: 5 isn't at least length 8
const out1 = Password(\"ez123\")
// ArkErrors: a number isn't a string
const out2 = Password(12345678)
``` |\n| **message** | ✅ a function accepting the result of `problem` in addition to other context and returning a complete description of the problem including the path at which it occurred
❌ may not apply to composite errors like unions | ```
const User = type({
\tpassword: \"string >= 8\"
}).configure({
\tmessage: ctx =>
\t\t`${ctx.propString || \"(root)\"}: ${ctx.actual} isn't ${ctx.expected}`
})
// ArkErrors: (root): a string isn't an object
const out1 = User(\"ez123\")
// `.configure` only applies shallowly, so the nested error isn't changed!
// ArkErrors: password must be at least length 8 (was 5)
const out2 = User({ password: \"ez123\" })
``` |\n\n#### [By Code](https://arktype.io/docs/configuration\\#by-code)\n\nErrors can also be configured by their associated `code` property at a _scope_ or _global_ level.\n\nFor example:\n\n```\nconst mod = type.module(\n\t{ isEven: \"number%2\" },\n\t{\n\t\tdivisor: {\n\t\t\t// the available `ctx` types will include data specific to your errors\n\t\t\texpected: ctx => `% ${ctx.rule} !== 0`,\n\t\t\tproblem: ctx => `${ctx.actual} ${ctx.expected}`\n\t\t}\n\t}\n)\n// ArkErrors: 3 % 2 !== 0\nmod.isEven(3)\n```\n\n#### [ArkErrors](https://arktype.io/docs/configuration\\#arkerrors)\n\nFor use cases like i18n that fall outside the scope of this composable message config, the `ArkErrors` array returned on validation failure contains `ArkError` instances that can be discriminated via calls like `.hasCode(\"divisor\")` and contain contextual data specific to that error type as well as getters for each composable error part.\n\nThese `ArkError` instances can be arbitrarily transformed and composed with an internationalization library. This is still a topic we're working on investigating and documenting, so please reach out with any questions or feedback!\n\n#### [Serialization](https://arktype.io/docs/configuration\\#serialization)\n\n`ArkErrors` and `ArkError` are JSON stringifiable via `JSON.stringify()` or `.toJSON()`.\n\nTwo additional properties provide structured access to errors grouped by path:\n\n```\nconst T = type({ n: \"number % 2 >= 2\" })\n\nconst out = T({ n: 1 })\n\nif (out instanceof type.errors) {\n\tout.flatByPath\n\t// { n: [{ data: 1, path: [\"n\"], code: \"divisor\", ... }, { data: 1, path: [\"n\"], code: \"min\", ... }] }\n\n\tout.flatProblemsByPath\n\t// { n: [\"must be even (was 1)\", \"must be at least 2 (was 1)\"] }\n}\n```\n\n`flatByPath` maps each path string to an array of `ArkError` objects (decomposing union errors into their individual branches). `flatProblemsByPath` maps each path string to an array of human-readable problem strings.\n\nUnhandled validation errors (e.g. via `.assert()`) throw a `TraversalError`, which extends `Error` with cleaner multi-error formatting and a non-enumerable `arkErrors` property for programmatic access.\n\n### [Keywords](https://arktype.io/docs/configuration\\#keywords)\n\nBuilt-in keywords like `string.email` can be globally configured.\n\nThis can be very helpful for customizing error messages without needing to create your own aliases or wrappers.\n\nconfig.ts\n\n```\nimport { configure } from \"arktype/config\"\n\nconfigure({\n\tkeywords: {\n\t\tstring: \"shorthand description\",\n\t\t\"string.email\": {\n\t\t\tactual: () => \"definitely fake\"\n\t\t}\n\t}\n})\n```\n\napp.ts\n\n```\nimport \"./config.ts\"\n// import your config file before arktype\nimport { type } from \"arktype\"\n\nconst User = type({\n\tname: \"string\",\n\temail: \"string.email\"\n})\n\nconst out = User({\n\t// ArkErrors: name must be shorthand description (was a number)\n\tname: 5,\n\t// ArkErrors: email must be an email address (was definitely fake)\n\temail: \"449 Canal St\"\n})\n```\n\nThe options you can provide here are identical to those used to [configure a Type directly](https://arktype.io/docs/expressions#meta), and can also be [extended at a type-level to include custom metadata](https://arktype.io/docs/configuration#metadata).\n\n### [Clone](https://arktype.io/docs/configuration\\#clone)\n\nBy default, before a [morph](https://arktype.io/docs/intro/morphs-and-more) is applied, ArkType will deeply clone the original input value with a built-in `deepClone` function that tries to make reasonable assumptions about preserving prototypes etc. The implementation of `deepClone` can be found [here](https://github.com/arktypeio/arktype/blob/main/ark/util/clone.ts).\n\nYou can provide an alternate clone implementation to the `clone` config option.\n\nconfig.ts\n\n```\nimport { configure } from \"arktype/config\"\n\nconfigure({ clone: structuredClone })\n```\n\napp.ts\n\n```\nimport \"./config.ts\"\n// import your config file before arktype\nimport { type } from \"arktype\"\n\n// will now create a new object using structuredClone\nconst UserForm = type({\n\tage: \"string.numeric.parse\"\n})\n```\n\nTo mutate the input object directly, you can set the `clone` config option to `false`.\n\nconfig.ts\n\n```\nimport { configure } from \"arktype/config\"\n\nconfigure({ clone: false })\n```\n\napp.ts\n\n```\nimport \"./config.ts\"\n// import your config file before arktype\nimport { type } from \"arktype\"\n\nconst UserForm = type({\n\tage: \"string.numeric.parse\"\n})\n\nconst formData = {\n\tage: \"42\"\n}\n\nconst out = UserForm(formData)\n\n// the original object's age key is now a number\nconsole.log(formData.age)\n```\n\n### [onUndeclaredKey](https://arktype.io/docs/configuration\\#onundeclaredkey)\n\nLike TypeScript, ArkType defaults to ignoring undeclared keys during validation. However, it also supports two additional behaviors:\n\n- `\"ignore\"` (default): Allow undeclared keys on input, preserve them on output\n- `\"delete\"`: Allow undeclared keys on input, delete them before returning output\n- `\"reject\"`: Reject input with undeclared keys\n\nThese behaviors can be associated with individual Types via the built-in `\"+\"` syntax (see [those docs](https://arktype.io/docs/objects#properties-undeclared) for more on how they work). You can also change the default globally:\n\nconfig.ts\n\n```\nimport { configure } from \"arktype/config\"\n\nconfigure({ onUndeclaredKey: \"delete\" })\n```\n\napp.ts\n\n```\nimport \"./config.ts\"\n// import your config file before arktype\nimport { type } from \"arktype\"\n\nconst UserForm = type({\n\tname: \"string\"\n})\n\n// out is now { name: \"Alice\" }\nconst out = UserForm({\n\tname: \"Alice\",\n\tage: \"42\"\n})\n```\n\n### [exactOptionalPropertyTypes](https://arktype.io/docs/configuration\\#exactoptionalpropertytypes)\n\nBy default, ArkType validates optional keys as if [TypeScript's `exactOptionalPropertyTypes` is set to `true`](https://www.typescriptlang.org/tsconfig/#exactOptionalPropertyTypes).\n\nSee an example\n\n```\nconst MyObj = type({\n\t\"key?\": \"number\"\n})\n\n// valid data\nconst validResult = MyObj({})\n\n// Error: key must be a number (was undefined)\nconst errorResult = MyObj({ key: undefined })\n```\n\nThis approach allows the most granular control over optionality, as `| undefined` can be added to properties that should accept it.\n\nHowever, if you have not enabled TypeScript's `exactOptionalPropertyTypes` setting, you may globally configure ArkType's `exactOptionalPropertyTypes` to `false` to match TypeScript's behavior. If you do this, we'd recommend making a plan to enable `exactOptionalPropertyTypes` in the future.\n\nconfig.ts\n\n```\nimport { configure } from \"arktype/config\"\n\n// since the default in ArkType is `true`, this will only have an effect if set to `false`\nconfigure({ exactOptionalPropertyTypes: false })\n```\n\napp.ts\n\n```\nimport \"./config.ts\"\n// import your config file before arktype\nimport { type } from \"arktype\"\n\nconst MyObj = type({\n\t\"key?\": \"number\"\n})\n\n// valid data\nconst validResult = MyObj({})\n\n// now also valid data (would be an error by default)\nconst secondResult = MyObj({ key: undefined })\n```\n\nexactOptionalPropertyTypes does not yet affect default values!\n\n```\nconst MyObj = type({\n\tkey: \"number = 5\"\n})\n\n// { key: 5 }\nconst omittedResult = MyObj({})\n\n// { key: undefined }\nconst undefinedResult = MyObj({ key: undefined })\n```\n\nSupport for this is tracked as part of [this broader configurable defaultability issue](https://github.com/arktypeio/arktype/issues/1390).\n\n### [jitless](https://arktype.io/docs/configuration\\#jitless)\n\nBy default, when a `Type` is instantiated, ArkType will precompile optimized validation logic that will run when the type is invoked. This behavior is disabled by default in environments that don't support `new Function`, e.g. Cloudflare Workers.\n\nIf you'd like to opt out of it for another reason, you can set the `jitless` config option to `true`.\n\nconfig.ts\n\n```\nimport { configure } from \"arktype/config\"\n\nconfigure({ jitless: true })\n```\n\napp.ts\n\n```\nimport \"./config.ts\"\n// import your config file before arktype\nimport { type } from \"arktype\"\n\n// will not be precompiled\nconst MyObject = type({\n\tfoo: \"string\"\n})\n```\n\n### [onFail](https://arktype.io/docs/configuration\\#onfail)\n\nIn some domains, you may always want to throw on failed validation or transform the result in some other way.\n\nBy specifying `onFail` in your global config, you can control what happens when you invoke a `Type` on invalid data:\n\nconfig.ts\n\n```\nimport { configure } from \"arktype/config\"\n\nconst config = configure({\n\tonFail: errors => errors.throw()\n})\n\n// be sure to specify both the runtime and static configs\n\ndeclare global {\n\tinterface ArkEnv {\n\t\tonFail: typeof config.onFail\n\t}\n}\n```\n\napp.ts\n\n```\nimport \"./config.ts\"\n// import your config file before arktype\nimport { type } from \"arktype\"\n\n// data is inferred as string- no need to discriminate!\nconst data = type.string(\"foo\")\n\n// now thrown instead of returned\n// ArkErrors: must be a string (was number)\nconst bad = type.string(5)\n```\n\n### [metadata](https://arktype.io/docs/configuration\\#metadata)\n\nAdditional arbitrary metadata can also be associated with a Type.\n\nIt can even be made type-safe via an interface extension ArkType exposes for this purpose:\n\n```\n// add this anywhere in your project\ndeclare global {\n\tinterface ArkEnv {\n\t\tmeta(): {\n\t\t\t// meta properties should always be optional\n\t\t\tsecretIngredient?: string\n\t\t}\n\t}\n}\n\n// now types you define can specify and access your metadata\nconst MrPingsSecretIngredientSoup = type({\n\tbroth: \"'miso' | 'vegetable'\",\n\tingredients: \"string[]\"\n}).configure({ secretIngredient: \"nothing!\" })\n```\n\n### [toJsonSchema](https://arktype.io/docs/configuration\\#tojsonschema)\n\nSome ArkType features don't have JSON Schema equivalents. By default, `toJsonSchema()` will throw in these cases.\n\nThis behavior can be configured granularly to match your needs.\n\n```\nconst T = type({\n\t\"[symbol]\": \"string\",\n\tbirthday: \"Date\"\n})\n\nconst schema = T.toJsonSchema({\n\tfallback: {\n\t\t// ✅ the \"default\" key is a fallback for any non-explicitly handled code\n\t\t// ✅ ctx includes \"base\" (represents the schema being generated) and other code-specific props\n\t\t// ✅ returning `ctx.base` will effectively ignore the incompatible constraint\n\t\tdefault: ctx => ctx.base,\n\t\t// handle specific incompatibilities granularly\n\t\tdate: ctx => ({\n\t\t\t...ctx.base,\n\t\t\ttype: \"string\",\n\t\t\tformat: \"date-time\",\n\t\t\tdescription: ctx.after ? `after ${ctx.after}` : \"anytime\"\n\t\t})\n\t}\n})\n\nconst result = {\n\t$schema: \"https://json-schema.org/draft/2020-12/schema\",\n\ttype: \"object\",\n\tproperties: {\n\t\t// Date instance is now a date-time string as specified by the `date` handler\n\t\tbirthday: { type: \"string\", format: \"date-time\", description: \"anytime\" }\n\t},\n\trequired: [\"birthday\"]\n\t// symbolic index signature ignored as specified by the `default` handler\n}\n```\n\na `default` handler can also be specified at the root of a `fallback` config:\n\n```\nconst T = type({\n\t\"[symbol]\": \"string\",\n\tbirthday: \"Date\"\n})\n\n//--- cut ---\n\nconst schema = T.toJsonSchema({\n\t// \"just make it work\"\n\tfallback: ctx => ctx.base\n})\n```\n\nThese options can also be set at a [global or scope-level](https://arktype.io/docs/configuration#levels).\n\n### [Fallback Codes](https://arktype.io/docs/configuration\\#fallback-codes)\n\nThis is the full list of configurable reasons `toJsonSchema()` can fail.\n\n| Code | Description |\n| --- | --- |\n| `arrayObject` | arrays with object properties |\n| `arrayPostfix` | arrays with postfix elements |\n| `defaultValue` | non-serializable default value |\n| `domain` | non-serializable type keyword (always `bigint` or `symbol`) |\n| `morph` | transformation |\n| `patternIntersection` | multiple regex constraints |\n| `predicate` | custom narrow function |\n| `proto` | non-serializable `instanceof` |\n| `symbolKey` | symbolic key on an object |\n| `unit` | non-serializable `===` reference (e.g. `undefined`) |\n| `date` | a Date instance (supersedes `proto` for Dates) |\n\n### [prototypes](https://arktype.io/docs/configuration\\#prototypes)\n\nWhen you `.infer` your Types, ArkType traverses them and extracts special values like morphs, e.g. `(In: string) => Out`.\n\nThough generally this is able to preserve the original type, it is inefficient and can accidentally expand certain object types.\n\nYou can use the type-level `prototypes` config to tell ArkType to treat those types as external:\n\n```\ndeclare global {\n\tinterface ArkEnv {\n\t\tprototypes(): MySpecialClass\n\t}\n}\n\nclass MySpecialClass {}\n\nconst\nconst T: Type\nT = type.instanceOf(MySpecialClass)\n```\n\n[Match\\\\\n\\\\\nPrevious Page](https://arktype.io/docs/match) [levels\\\\\n\\\\\nNext Page](https://arktype.io/docs/configuration#levels)","metadata":{"ogTitle":"ArkType Docs","title":"Configuration","next-size-adjust":"","ogDescription":"TypeScript's 1:1 validator, optimized from editor to runtime","og:image:height":"600","og:url":"https://arktype.io/","og:image":"https://arktype.io/image/ogDocs.png","keywords":"ArkType,TypeScript,JavaScript,runtime validation,schema,type-safe,validator,syntax","og:image:width":"1200","twitter:image:width":"1200","ogSiteName":"ArkType","og:site_name":"ArkType","viewport":"width=device-width, initial-scale=1","ogImage":"https://arktype.io/image/ogDocs.png","language":"en","og:description":"TypeScript's 1:1 validator, optimized from editor to runtime","og:title":"ArkType Docs","twitter:description":"TypeScript's 1:1 validator, optimized from editor to runtime","twitter:title":"ArkType Docs","twitter:image":"https://arktype.io/image/ogDocs.png","twitter:image:height":"600","ogUrl":"https://arktype.io/","twitter:card":"summary_large_image","og:type":"website","favicon":"https://arktype.io/image/favicon.svg","scrapeId":"019d3953-d719-772d-b1bb-c0b60bc142b7","sourceURL":"https://arktype.io/docs/configuration","url":"https://arktype.io/docs/configuration","statusCode":200,"contentType":"text/html; charset=utf-8","timezone":"America/New_York","proxyUsed":"basic","cacheState":"miss","indexId":"5e0f4856-bdfe-485e-a651-67ca1f3cc3ba","creditsUsed":1}}]}}