{"id":13990773,"url":"https://github.com/vitalics/ajv-ts","last_synced_at":"2025-03-15T14:05:15.091Z","repository":{"id":196629294,"uuid":"696151728","full_name":"vitalics/ajv-ts","owner":"vitalics","description":"First-class ajv typescript JSON-schema builder inspired from Zod","archived":false,"fork":false,"pushed_at":"2024-09-13T21:53:42.000Z","size":493,"stargazers_count":46,"open_issues_count":3,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-02-28T03:23:22.281Z","etag":null,"topics":["ajv","ajv-validation","builder","jsonschema","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/ajv-ts","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/vitalics.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"open_collective":"ajv-typescript"}},"created_at":"2023-09-25T07:35:51.000Z","updated_at":"2025-02-20T11:32:49.000Z","dependencies_parsed_at":"2023-09-26T17:09:41.426Z","dependency_job_id":"9c3ae94e-ac64-4553-bfb4-0e54ea83950e","html_url":"https://github.com/vitalics/ajv-ts","commit_stats":{"total_commits":60,"total_committers":2,"mean_commits":30.0,"dds":"0.33333333333333337","last_synced_commit":"99a3a4f289e146ee5f24215fe2372290fdbf9f5f"},"previous_names":["vitalics/ajv-ts"],"tags_count":21,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalics%2Fajv-ts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalics%2Fajv-ts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalics%2Fajv-ts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vitalics%2Fajv-ts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vitalics","download_url":"https://codeload.github.com/vitalics/ajv-ts/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243738981,"owners_count":20340001,"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":["ajv","ajv-validation","builder","jsonschema","typescript"],"created_at":"2024-08-09T13:03:12.901Z","updated_at":"2025-03-15T14:05:15.068Z","avatar_url":"https://github.com/vitalics.png","language":"TypeScript","funding_links":["https://opencollective.com/ajv-typescript"],"categories":["TypeScript","typescript"],"sub_categories":[],"readme":"# ajv-ts\n\n## Table of Contents\n\n- [ajv-ts](#ajv-ts)\n  - [Table of Contents](#table-of-contents)\n  - [Zod unsupported APIs/differences](#zod-unsupported-apisdifferences)\n  - [Installation](#installation)\n  - [Basic usage](#basic-usage)\n  - [Base schema](#base-schema)\n    - [examples](#examples)\n    - [custom](#custom)\n    - [meta](#meta)\n  - [JSON schema overriding](#json-schema-overriding)\n  - [Defaults](#defaults)\n  - [Primitives](#primitives)\n  - [Constant values(literals)](#constant-valuesliterals)\n  - [String](#string)\n    - [Typescript features](#typescript-features)\n  - [Numbers](#numbers)\n    - [Types](#types)\n      - [Number](#number)\n      - [Int](#int)\n    - [Formats](#formats)\n      - [int32](#int32)\n      - [int64](#int64)\n      - [float](#float)\n      - [double](#double)\n    - [Typescript features](#typescript-features-1)\n  - [BigInts](#bigints)\n  - [NaNs](#nans)\n  - [Dates](#dates)\n  - [Enums](#enums)\n    - [Autocompletion](#autocompletion)\n  - [Native enums](#native-enums)\n  - [Optionals](#optionals)\n  - [Nullables](#nullables)\n  - [Objects](#objects)\n    - [`.keyof`](#keyof)\n    - [`.extend`](#extend)\n    - [`.merge`](#merge)\n    - [`.pick`/`.omit`](#pickomit)\n    - [`.partial`](#partial)\n    - [`.required`](#required)\n    - [`.requiredFor`](#requiredfor)\n    - [`.partialFor`](#partialfor)\n    - [`.passthrough`](#passthrough)\n    - [`.strict`](#strict)\n    - [`.dependentRequired`](#dependentrequired)\n    - [`.rest`](#rest)\n  - [Arrays](#arrays)\n    - [`.addItems`](#additems)\n    - [`.element`](#element)\n    - [`.nonempty`](#nonempty)\n    - [`.min`/`.max`/`.length`/`.minLength`/`.maxLength`](#minmaxlengthminlengthmaxlength)\n    - [Typescript features](#typescript-features-2)\n    - [`.unique`](#unique)\n    - [`.contains`/`.minContains`](#containsmincontains)\n  - [Tuples](#tuples)\n  - [unions/or](#unionsor)\n  - [Intersections/and](#intersectionsand)\n  - [Set](#set)\n  - [Map](#map)\n  - [`any`/`unknown`](#anyunknown)\n  - [`never`](#never)\n  - [`not`/`exclude`](#notexclude)\n  - [Custom Ajv instance](#custom-ajv-instance)\n  - [`custom` shema definition](#custom-shema-definition)\n  - [Transformations](#transformations)\n    - [Preprocess](#preprocess)\n    - [Postprocess](#postprocess)\n  - [Error handling](#error-handling)\n    - [Error Map](#error-map)\n    - [refine](#refine)\n\nJSON schema builder like in ZOD-like API\n\n\u003e TypeScript schema validation with static type inference!\n\nReasons to install `ajv-ts` instead of `zod`\n\n1. Less code. `zod` has 4k+ lines of code\n2. not JSON-schema compatibility out of box (but you can install some additional plugins)\n3. we not use own parser, just `ajv`, which wild spreadable(90M week installations for `ajv` vs 5M for `zod`)\n4. Same typescript types and API\n5. You can inject own `ajv` instance!\n\nWe inspired API from `zod`. So you just can reimport you api and that's it!\n\n## Zod unsupported APIs/differences\n\n1. `s.date`, `s.symbol`, `s.void`, `s.void`, `s.bigint`, `s.function` does not supported. Since JSON-schema doesn't define `Date`, `Symbol`, `void`, `function`, `Set`, `Map` as separate type. For strings you can use `s.string().format('date-time')` or other JSON-string format compatibility: https://json-schema.org/understanding-json-schema/reference/string.html\n2. `s.null` === `s.undefined` - same types, but helps typescript with autocompletion\n3. `z.enum` and `z.nativeEnum` it's a same as `s.enum`. We make enums fully compatible, it can be array of strings or structure defined with `enum` keyword in typescript\n4. Exporting `s` isntead of `z`, since `s` - is a shorthand for `schema`\n5. `z.custom` is not supported\n6. `z.literal` === `s.const`.\n\n## Installation\n\n```bash\nnpm install ajv-ts       # npm\nyarn add ajv-ts          # yarn\nbun add ajv-ts           # bun\npnpm add ajv-ts          # pnpm\n```\n\n## Basic usage\n\nCreating a simple string schema\n\n```typescript\nimport { s } from \"ajv-ts\";\n\n// creating a schema for strings\nconst mySchema = s.string();\n\n// parsing\nmySchema.parse(\"tuna\"); // =\u003e \"tuna\"\nmySchema.parse(12); // =\u003e throws Ajv Error\n\n// \"safe\" parsing (doesn't throw error if validation fails)\nmySchema.safeParse(\"tuna\"); // =\u003e { success: true; data: \"tuna\" }\nmySchema.safeParse(12); // =\u003e { success: false; error: AjvError }\n```\n\nCreating an object schema\n\n```ts\nimport { s } from \"ajv-ts\";\n\nconst User = s.object({\n  username: s.string(),\n});\n\nUser.parse({ username: \"Ludwig\" });\n\n// extract the inferred type\ntype User = s.infer\u003ctypeof User\u003e;\n// { username: string }\n```\n\n## Base schema\n\nEvery schema inherits these class with next methods/properties\n\n### examples\n\nThe `examples` keyword is a place to provide an array of examples that validate against the schema. This isn’t used for validation, but may help with explaining the effect and purpose of the schema to a reader. Each entry should validate against the schema in which it resides, but that isn’t strictly required. There is no need to duplicate the default value in the examples array, since default will be treated as another example.\n\n**Note**: While it is recommended that the examples validate against the subschema they are defined in, this requirement is not strictly enforced.\n\nUsed to demonstrate how data should conform to the schema.\nexamples does not affect data validation but serves as an informative annotation.\n\n```ts\ns.string().examples([\"str1\", 'string 2']) // OK\ns.number().examples([\"str1\", 'string 2']) // Error\ns.number().examples([1, 2, 3]) // OK\ns.number().examples(1, 2, 3) // OK\n```\n\n### custom\n\nAdd custom schema key-value definition.\n\nset custom JSON-schema field. Useful if you need to declare something but no api founded for built-in solution.\n\nExample: `If-Then-Else` you cannot declare without `custom` method.\n\n```ts\nconst myObj = s.object({\n foo: s.string(),\n bar: s.string()\n}).custom('if', {\n \"properties\": {\n   \"foo\": { \"const\": \"bar\" }\n },\n \"required\": [\"foo\"]\n }).custom('then', { \"required\": [\"bar\"] })\n```\n\n### meta\n\nAdds meta information fields in your schema, such as `deprecated`, `description`, `$id`, `title` and more!\n\nExample:\n\n```ts\nconst numSchema = s.number().meta({\n  title: 'my number schema',\n  description: 'Some description',\n  deprecated: true\n})\n\nnumSchema.schema // {type: 'number', title: 'my number schema', description: 'Some description', deprecated: true }\n```\n\n## JSON schema overriding\n\nIn case of you have alredy defined JSON-schema, you create an `any/object/number/string/boolean/null` schema and set `schema` property from your schema.\n\nExample:\n\n```ts\nimport s from 'ajv-ts'\n\nconst SchemaFromSomewhere = {\n \"title\": \"Example Schema\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"name\": {\n      \"type\": \"string\"\n    },\n    \"age\": {\n      \"description\": \"Age in years\",\n      \"type\": \"integer\",\n      \"minimum\": 0\n    },\n  },\n  \"required\": [\"name\", \"age\"]\n}\n\ntype MySchema = {\n  name: string;\n  age: number\n}\n\nconst AnySchema = s.any()\nAnySchema.schema = SchemaFromSomewhere\n\nAnySchema.parse({name: 'hello', age: 18}) // OK, since we override JSON-schema\n\n// or using object\nconst Obj = s.object\u003cMySchema\u003e()\nObj.schema = SchemaFromSomewhere\n\nObj.parse({name: 'hello', age: 18}) // OK\n\n```\n\n## Defaults\n\nOption `default` keywords throws exception during schema compilation when used in:\n\n- not in properties or items subschemas\n- in schemas inside anyOf, oneOf and not ([#42](https://github.com/ajv-validator/ajv/issues/42))\n- in if schema\n- in schemas generated by user-defined macro keywords.\n\nThis means only `object()` and `array()` buidlers are supported.\n\nExample\n\n```ts\nimport s from 'ajv-ts'\nconst Person = s.object({\n  age: s.int().default(18)\n})\nPerson.parse({}) // { age: 18 }\n```\n\n## Primitives\n\n```ts\nimport { s } from \"ajv-ts\";\n\n// primitive values\ns.string();\ns.number();\ns.boolean();\n\n// empty types\ns.undefined();\ns.null();\n\n// allows any value\ns.any();\ns.unknown();\n```\n\n## Constant values(literals)\n\n```ts\nconst tuna = s.const(\"tuna\");\nconst twelve = s.const(12);\nconst tru = s.const(true);\n\n// retrieve literal value\ntuna.value; // \"tuna\"\n```\n\n## String\n\nincludes a handful of string-specific validations.\n\n```ts\n// validations\ns.string().maxLength(5);\ns.string().minLength(5);\ns.string().length(5);\ns.string().format('email');\ns.string().format('url');\ns.string().regex(regex);\ns.string().format('date-time');\ns.string().format('ipv4');\n\n// transformations\ns.string().postprocess(v =\u003e v.trim());\ns.string().postprocess(v =\u003e v.toLowerCase());\ns.string().postprocess(v =\u003e v.toUpperCase());\n```\n\n### Typescript features\n\n\u003e from \u003e=0.7.x\n\nUnlike zod - we make typescript validation for `minLength` and `maxLength`. That means you cannot create schema when expected length are negative number or `maxLength \u003c minLength`.\n\nHere is few examples:\n\n```typescript\ns.string().minLength(3).maxLength(1) // [never, \"RangeError: MaxLength less than MinLength\", \"MinLength: 3\", \"MaxLength: 1\"]\n\ns.string().length(-1) // [never, \"TypeError: expected positive integer. Received: '-1'\"]\n```\n\n## Numbers\n\nincludes a handful of number-specific validations.\n\n```ts\ns.number().gt(5);\ns.number().gte(5); // alias .min(5)\ns.number().lt(5);\ns.number().lte(5); // alias .max(5)\n\ns.number().int(); // value must be an integer\n\ns.number().positive(); //     \u003e 0\ns.number().nonnegative(); //  \u003e= 0\ns.number().negative(); //     \u003c 0\ns.number().nonpositive(); //  \u003c= 0\n\ns.number().multipleOf(5); // Evenly divisible by 5. Alias .step(5)\n```\n\n### Types\n\n#### Number\n\nNumber - any number type\n\n```ts\ns.number()\n// same as\ns.number().number()\n```\n\n#### Int\n\nOnly integers values.\n\nNote: we check in runtime non-integer format (`float`, `double`) and give an error.\n\n```ts\ns.number().int()\n// or\ns.number().integer()\n// or\ns.int()\n```\n\n### Formats\n\nDefines in [ajv-formats](https://ajv.js.org/packages/ajv-formats.html#formats) package\n\n#### int32\n\nSigned 32 bits integer according to the [openApi 3.0.0 specification](https://spec.openapis.org/oas/v3.0.0#data-types)\n\n#### int64\n\nSigned 64 bits according to the [openApi 3.0.0 specification](https://spec.openapis.org/oas/v3.0.0#data-types)\n\n#### float\n\nfloat: float according to the [openApi 3.0.0 specification](https://spec.openapis.org/oas/v3.0.0#data-types)\n\n#### double\n\ndouble: double according to the [openApi 3.0.0 specification](https://spec.openapis.org/oas/v3.0.0#data-types)\n\n### Typescript features\n\n\u003e from \u003e= 0.8\n\nWe make validation for number `type`, `format`, `minValue` and `maxValue` fields. That means we handle it in our side so you get an error for invalid values.\n\nExamples:\n\n```ts\ns.number().format('float').int() // error in type!\ns.int().const(3.4) // error in type!\ns.number().int().format('float') // error in format!\ns.number().int().format('double') // error in format!\n\n// ranges are also check for possibility\n\ns.number().min(5).max(3) // error in range!\ns.number().min(3).max(5).const(10) // error in constant!\n```\n\n## BigInts\n\nNot supported\n\n## NaNs\n\nNot supported\n\n## Dates\n\nNot supported, but you can pass `parseDates` in your AJV instance.\n\n## Enums\n\n```ts\nconst FishEnum = s.enum([\"Salmon\", \"Tuna\", \"Trout\"]);\ntype FishEnum = s.infer\u003ctypeof FishEnum\u003e;\n// 'Salmon' | 'Tuna' | 'Trout'\n```\n\n```ts\nconst VALUES = [\"Salmon\", \"Tuna\", \"Trout\"] as const;\nconst FishEnum = s.enum(VALUES);\n```\n\n### Autocompletion\n\nTo get autocompletion with a enum, use the `.enum` property of your schema:\n\n```ts\nFishEnum.enum.Salmon; // =\u003e autocompletes\n\nFishEnum.enum;\n/*\n=\u003e {\n  Salmon: \"Salmon\",\n  Tuna: \"Tuna\",\n  Trout: \"Trout\",\n}\n*/\n```\n\nYou can also retrieve the list of options as a tuple with the .options property:\n\n```ts\nFishEnum.options; // [\"Salmon\", \"Tuna\", \"Trout\"];\n```\n\n## Native enums\n\n**Numeric enums:**\n\n```ts\nenum Fruits {\n  Apple,\n  Banana,\n}\n\nconst FruitEnum = s.enum(Fruits);\ntype FruitEnum = s.infer\u003ctypeof FruitEnum\u003e; // Fruits\n\nFruitEnum.parse(Fruits.Apple); // passes\nFruitEnum.parse(Fruits.Banana); // passes\nFruitEnum.parse(0); // passes\nFruitEnum.parse(1); // passes\nFruitEnum.parse(3); // fails\n```\n\n**String enums:**\n\n```ts\nenum Fruits {\n  Apple = \"apple\",\n  Banana = \"banana\",\n  Cantaloupe, // you can mix numerical and string enums\n}\n\nconst FruitEnum = s.enum(Fruits);\ntype FruitEnum = s.infer\u003ctypeof FruitEnum\u003e; // Fruits\n\nFruitEnum.parse(Fruits.Apple); // passes\nFruitEnum.parse(Fruits.Cantaloupe); // passes\nFruitEnum.parse(\"apple\"); // passes\nFruitEnum.parse(\"banana\"); // passes\nFruitEnum.parse(0); // passes\nFruitEnum.parse(\"Cantaloupe\"); // pass\n```\n\n**Const enums:**\n\nThe `.enum()` function works for as const objects as well. ⚠️ as const requires TypeScript 3.4+!\n\n```ts\nconst Fruits = {\n  Apple: \"apple\",\n  Banana: \"banana\",\n  Cantaloupe: 3,\n} as const;\n\nconst FruitEnum = s.enum(Fruits);\ntype FruitEnum = s.infer\u003ctypeof FruitEnum\u003e; // \"apple\" | \"banana\" | 3\n\nFruitEnum.parse(\"apple\"); // passes\nFruitEnum.parse(\"banana\"); // passes\nFruitEnum.parse(3); // passes\nFruitEnum.parse(\"Cantaloupe\"); // fails\n```\n\nYou can access the underlying object with the .enum property:\n\n```ts\nFruitEnum.enum.Apple; // \"apple\"\n```\n\n## Optionals\n\nYou can make any schema optional with `s.optional()`. This wraps the schema in a `Optional` instance and returns the result.\n\n```ts\nconst schema = s.string().optional();\n\nschema.parse(undefined); // =\u003e returns undefined\ntype A = s.infer\u003ctypeof schema\u003e; // string | undefined\n```\n\n## Nullables\n\n```ts\nconst nullableString = s.string().nullable();\nnullableString.parse(\"asdf\"); // =\u003e \"asdf\"\nnullableString.parse(null); // =\u003e null\nnullableString.parse(undefined); // throws error\n```\n\n## Objects\n\n```ts\n// all properties are required by default\nconst Dog = s.object({\n  name: s.string(),\n  age: s.number(),\n});\n\n// extract the inferred type like this\ntype Dog = s.infer\u003ctypeof Dog\u003e;\n\n// equivalent to:\ntype Dog = {\n  name: string;\n  age: number;\n};\n```\n\n### `.keyof`\n\nUse `.keyof` to create a `Enum` schema from the keys of an object schema.\n\n```ts\nconst keySchema = Dog.keyof();\nkeySchema; // Enum\u003c[\"name\", \"age\"]\u003e\n```\n\n### `.extend`\n\nYou can add additional fields to an object schema with the `.extend` method.\n\n```ts\nconst DogWithBreed = Dog.extend({\n  breed: s.string(),\n});\n```\n\nYou can use `.extend` to overwrite fields! Be careful with this power!\n\n### `.merge`\n\nEquivalent to `A.extend(B.schema)`.\n\n```ts\nconst BaseTeacher = s.object({ students: s.array(s.string()) });\nconst HasID = s.object({ id: s.string() });\n\nconst Teacher = BaseTeacher.merge(HasID);\ntype Teacher = s.infer\u003ctypeof Teacher\u003e; // =\u003e { students: string[], id: string }\n```\n\n### `.pick`/`.omit`\n\nInspired by TypeScript's built-in `Pick` and `Omit` utility types, all object schemas have `.pick` and `.omit` methods that return a modified version. Consider this Recipe schema:\n\n```ts\nconst Recipe = s.object({\n  id: s.string(),\n  name: s.string(),\n  ingredients: s.array(s.string()),\n});\n```\n\nTo only keep certain keys, use .pick .\n\n```ts\nconst JustTheName = Recipe.pick({ name: true });\ntype JustTheName = s.infer\u003ctypeof JustTheName\u003e;\n// =\u003e { name: string }\n```\n\nTo remove certain keys, use `.omit` .\n\n```ts\nconst NoIDRecipe = Recipe.omit({ id: true });\n\ntype NoIDRecipe = s.infer\u003ctypeof NoIDRecipe\u003e;\n// =\u003e { name: string, ingredients: string[] }\n```\n\n### `.partial`\n\nInspired by the built-in TypeScript utility type `Partial`, the .partial method makes all properties optional.\n\nStarting from this object:\n\n```ts\nconst user = s.object({\n  email: s.string(),\n  username: s.string(),\n});\n// { email: string; username: string }\nWe can create a partial version:\n\nconst partialUser = user.partial();\n// { email?: string | undefined; username?: string | undefined }\nYou can also specify which properties to make optional:\n\nconst optionalEmail = user.partial({\n  email: true,\n});\n/*\n{\n  email?: string | undefined;\n  username: string\n}\n*/\n```\n\n### `.required`\n\nContrary to the `.partial` method, the `.required` method makes all properties required.\n\nStarting from this object:\n\n```ts\nconst user = z\n  .object({\n    email: s.string(),\n    username: s.string(),\n  })\n  .partial();\n// { email?: string | undefined; username?: string | undefined }\n```\n\nWe can create a required version:\n\n```ts\nconst requiredUser = user.required();\n// { email: string; username: string }\nYou can also specify which properties to make required:\n\nconst requiredEmail = user.required({\n  email: true,\n});\n/*\n{\n  email: string;\n  username?: string | undefined;\n}\n*/\n```\n\n### `.requiredFor`\n\nAccepts keys which are required. Set `requiredProperties` for your JSON-schema\n\n```ts\nconst O = s.object({\n  first: s.number().optional(),\n  second: s.string().optional()\n}).requiredFor('first')\n\ntype O = s.infer\u003ctypeof O\u003e // {first: number, second?: string}\n```\n\n### `.partialFor`\n\nAccepts keys which are partial. unset properties from `required` schema field in your JSON-schema\n\n```ts\nconst O = s.object({\n  first: s.number().optional(),\n  second: s.string().optional()\n}).required().partialFor('second')\n\ntype O = s.infer\u003ctypeof O\u003e // {first: number, second?: string}\n```\n\n### `.passthrough`\n\nBy default object schemas strip out unrecognized keys during parsing.\n\n```ts\nconst person = s.object({\n  name: s.string(),\n});\n\nperson.parse({\n  name: \"bob dylan\",\n  extraKey: 61,\n});\n// =\u003e { name: \"bob dylan\" }\n// extraKey has been stripped\n```\n\nInstead, if you want to pass through unknown keys, use `.passthrough()` .\n\n```ts\nperson.passthrough().parse({\n  name: \"bob dylan\",\n  extraKey: 61,\n});\n// =\u003e { name: \"bob dylan\", extraKey: 61 }\n```\n\n### `.strict`\n\nBy default JSON object schemas allow to pass unrecognized keys during parsing. You can disallow unknown keys with `.strict()` . If there are any unknown keys in the input - will throw an error.\n\n```ts\nconst person = z\n  .object({\n    name: s.string(),\n  })\n  .strict();\n\nperson.parse({\n  name: \"bob dylan\",\n  extraKey: 61,\n});\n// =\u003e throws ZodError\n```\n\n### `.dependentRequired`\n\nThe `dependentRequired` keyword conditionally requires that\ncertain properties must be present if a given property is\npresent in an object. For example, suppose we have a schema\nrepresenting a customer. If you have their \"credit card number\",\nyou also want to ensure you have a \"billing address\".\nIf you don't have their credit card number, a \"billing address\"\noperty\non another using the `dependentRequired` keyword.\nThe value of the `dependentRequired` keyword is an object.\nEach entry in the object maps from the name of a property, p,\nto an array of strings listing properties that are `required`\nif p is present.\n\n```ts\nconst Test1 = s.object({\n  name: s.string(),\n  credit_card: s.number(),\n  billing_address: s.string(),\n  }).requiredFor('name').dependentRequired({\n    credit_card: ['billing_address'],\n  })\n\n/**\nTest1.schema === {\n    \"type\": \"object\",\n    \"properties\": {\n      \"name\": { \"type\": \"string\" },\n      \"credit_card\": { \"type\": \"number\" },\n      \"billing_address\": { \"type\": \"string\" }\n    },\n    \"required\": [\"name\"],\n    \"dependentRequired\": {\n      \"credit_card\": [\"billing_address\"]\n    }\n  }\n*/\n```\n\n### `.rest`\n\nThe `additionalProperties` keyword is used to control the handling of extra stuff, that is, `properties` whose names are\nnot listed in the `properties` keyword or match any of the regular expressions in the `patternProperties` keyword.\nBy default any additional properties are allowed.\n\nIf you need to set `additionalProperties=false` use [`strict`](#strict) method\n\n```ts\nconst Test = s.object({\n  street_name: s.string(),\n  street_type: s.enum([\"Street\", \"Avenue\", \"Boulevard\"])\n}).rest(s.string())\n\nTest.schema === {\n  \"type\": \"object\",\n  \"properties\": {\n    \"street_name\": { \"type\": \"string\" },\n    \"street_type\": { \"enum\": [\"Street\", \"Avenue\", \"Boulevard\"] }\n  },\n  \"additionalProperties\": { \"type\": \"string\" }\n}\n```\n\n## Arrays\n\n```typescript\nconst stringArray = s.array(s.string());\ntype StringArray = s.infer\u003ctypeof stringArray\u003e // string[]\n```\n\nOr it's invariant\n\n```ts\nconst stringArray = s.string().array();\ntype StringArray = s.infer\u003ctypeof stringArray\u003e // string[]\n```\n\nOr you can pass empty schema\n\n```ts\nconst empty = s.array()\n\ntype Empty = s.infer\u003ctypeof empty\u003e // unknown[]\n```\n\n### `.addItems`\n\npush(append) schema to array(parent) schema.\n\nExample:\n\n```ts\nimport s from 'ajv-ts'\n\nconst empty = s.array()\nconst stringArr = empty.addItems(s.string())\n\nstringArr.schema // {type: 'array', items: [{ type: 'string' }]}\n```\n\n### `.element`\n\nUse `.element` to access the schema for an element of the array.\n\n```ts\nstringArray.element; // =\u003e string schema, not array schema\n```\n\n### `.nonempty`\n\nIf you want to ensure that an array contains at least one element, use `.nonempty()`.\n\n```ts\nconst nonEmptyStrings = s.array(s.string()).nonempty();\n// the inferred type is now\n// [string, ...string[]]\n\nnonEmptyStrings.parse([]); // throws: \"Array cannot be empty\"\nnonEmptyStrings.parse([\"Ariana Grande\"]); // passes\n```\n\n### `.min`/`.max`/`.length`/`.minLength`/`.maxLength`\n\n```ts\ns.string().array().min(5); // must contain 5 or more items\ns.string().array().max(5); // must contain 5 or fewer items\ns.string().array().length(5); // must contain 5 items exactly\n```\n\nUnlike `.nonempty()` these methods do not change the inferred type.\n\n### Typescript features\n\n\u003e from \u003e=0.7.x\n\nUnlike zod - we make typescript validation for `minLength` and `maxLength`. That means you cannot create schema when expected length are not positive number or `maxLength \u003c minLength`.\n\nHere is few examples:\n\n```typescript\ns.string().array().minLength(3).maxLength(1) // [never, \"RangeError: MaxLength less than MinLength\", \"MinLength: 2\", \"MaxLength: 1\"]\n\ns.string().array().length(-1) // [never, \"TypeError: expected positive integer. Received: '-2'\"]\n```\n\n### `.unique`\n\nSet the `uniqueItems` keyword to `true`.\n\n```ts\nconst UniqueNumbers = s.array(s.number()).unique()\n\nUniqueNumbers.parse([1,2,3,4]) // Ok\nUniqueNumbers.parse([1,2,3,3]) // Error\n```\n\n### `.contains`/`.minContains`\n\n## Tuples\n\nUnlike arrays, tuples have a fixed number of elements and each element can have a different type.\n\n```ts\nconst athleteSchema = s.tuple([\n  s.string(), // name\n  s.number(), // jersey number\n  s.object({\n    pointsScored: s.number(),\n  }), // statistics\n]);\n\ntype Athlete = s.infer\u003ctypeof athleteSchema\u003e;\n// type Athlete = [string, number, { pointsScored: number }]\n```\n\nA variadic (\"rest\") argument can be added with the .rest method.\n\n```ts\nconst variadicTuple = s.tuple([s.string()]).rest(s.number());\nconst result = variadicTuple.parse([\"hello\", 1, 2, 3]);\n// =\u003e [string, ...number[]];\n```\n\n## unions/or\n\nincludes a built-in s.union method for composing \"OR\" types.\n\nThis function accepts array of schemas by spread argument.\n\n```ts\nconst stringOrNumber = s.union(s.string(), s.number());\n\nstringOrNumber.parse(\"foo\"); // passes\nstringOrNumber.parse(14); // passes\n```\n\nOr it's invariant - `or` function:\n\n```ts\ns.number().or(s.string()) // number | string\n```\n\n## Intersections/and\n\nIntersections are \"logical AND\" types. This is useful for intersecting two object types.\n\n```ts\nconst Person = s.object({\n  name: s.string(),\n});\n\nconst Employee = s.object({\n  role: s.string(),\n});\n\nconst EmployedPerson = s.intersection(Person, Employee);\n\n// equivalent to:\nconst EmployedPerson = Person.and(Employee);\n\n// equivalent to:\nconst EmployedPerson = and(Person, Employee);\n```\n\nThough in many cases, it is recommended to use `A.merge(B)` to merge two objects. The `.merge` method returns a new Object instance, whereas `A.and(B)` returns a less useful Intersection instance that lacks common object methods like `pick` and `omit`.\n\n```ts\nconst a = s.union(s.number(), s.string());\nconst b = s.union(s.number(), s.boolean());\nconst c = s.intersection(a, b);\n\ntype c = s.infer\u003ctypeof c\u003e; // =\u003e number\n```\n\n## Set\n\nNot supported\n\n## Map\n\nNot supported\n\n## `any`/`unknown`\n\nAny and unknown defines `{}` (empty object) as JSON-schema. very useful if you need to create something specific\n\n## `never`\n\nNever defines using `{not: {}}` (empty not). Any given json schema will be fails.\n\n## `not`/`exclude`\n\nHere is a 2 differences between `not` and `exclude`.\n\n- `not` method wrap given schema with `not`\n- `exclude(schema)` - add `not` keyword for incoming `schema` argument\n\nExample:\n\n```ts\nimport s from 'ajv-ts'\n\n// not\nconst notAString = s.string().not() // or s.not(s.string())\n\nnotAString.valid('random string') // false, this is a string\nnotAString.valid(123) // true\n\n// exclude\nconst notJohn = s.string().exclude(s.const('John'))\n\nnotJohn.valid('random string') // true\nnotJohn.valid('John') // false, this is John\n\n// advanced usage\n\nconst str = s.string\u003c'John' | 'Mary'\u003e().exclude(s.const('John'))\ns.infer\u003ctypeof str\u003e // 'Mary'\n```\n\n## Custom Ajv instance\n\nIf you need to create a custom AJV Instance, you can use `create` or `new` function.\n\n```ts\nimport addKeywords from 'ajv-keywords';\nimport schemaBuilder from 'ajv-ts'\n\nconst myAjvInstance = new Ajv({parseDate: true})\n\nexport const s = schemaBuilder.create(myAjvInstance)\n\n// later:\ns.string().dateTime().parse(new Date()) // 2023-10-05T19:31:57.610Z\n```\n\n## `custom` shema definition\n\nIf you need to append something specific to you schema, you can use `custom` method.\n\n```ts\nconst condition = s.any() // schema: {}\n\nconst withIf = condition.custom('if', {properties: {foo: {type: 'string'}}})\n\nwithIf.schema // {if: {properties: {foo: {type: 'string'}}}}\n\n```\n\n## Transformations\n\n### Preprocess\n\nfunction thant will be applied before calling `parse` method, It can helps you to modify incomining data\n\nBe careful with this information\n\n```ts\nconst ToString = s.string().preprocess(x =\u003e {\n  if(x instanceof Date){\n    return x.toISOString()\n  }\n  return x\n}, s.string())\n\nToString.parse(12) // error: expects a string\n\nToString.parse(new Date()) // 2023-09-26T13:44:46.497Z\n\n```\n\n### Postprocess\n\nfunction thant will be applied after calling `parse` method.\n\n```ts\nconst ToString = s.number().postprocess(x =\u003e String(x), s.string())\n\nToString.parse(12) // after parse we get \"12\" 12 =\u003e \"12\". \n\nToString.parse({}) // error: expects number. Postprocess has not been called\n```\n\n## Error handling\n\nError handling and error maps based from official package [`ajv-errors`](https://ajv.js.org/packages/ajv-errors.html#templates). You can check in out from there.\n\nDefines custom error message for not valid schema.\n\n```ts\nconst S1 = s.string().error('Im fails unexpected')\nS1.parse({}) // throws: Im fails unexpected\n```\n\n### Error Map\n\nYou can define custom error map.\nIn most cases you can pass just a string for invalidation.\nAlso, you can pass error map.\n\nExample:\n\n```ts\nimport s from 'ajv-ts'\n\nconst s1 = s.string().error({ _: \"any error here\" })\n\ns.parse(123) // throws \"any error here\"\n\ns.string().error({ _: \"any error here\", type: \"not a string. Custom\" })\n\ns.parse(123) // throws \"not a string. Custom\"\n\nconst Obj = s\n  .object({ foo: s.string(),})\n  .strict()\n  .error({ additionalProperties: \"Not expected to pass additional props\" });\n\nObj.parse({foo: 'ok', bar: true}) // throws \"Not expected to pass additional props\"\n```\n\n```ts\nconst Schema = s\n    .object({\n      foo: s.integer().minimum(2),\n      bar: s.string().minLength(2),\n    })\n    .strict()\n    .error({\n      properties: {\n        foo: \"data.foo should be integer \u003e= 2\",\n        bar: \"data.bar should be string with length \u003e= 2\",\n      },\n    });\nSchema.parse({ foo: 1, bar: \"a\" }) // throws: \"data.foo should be integer \u003e= 2\"\n```\n\n### refine\n\nInspired from `zod`. Set custom validation. Any result exept `undefined` will throws(or exposed for `safeParse` method).\n\n```ts\nimport s from 'ajv-ts'\n// example: object with only 1 \"active element\"\nconst Schema = s.object({\nactive: s.boolean(),\nname: s.string()\n}).array().refine((arr) =\u003e {\n  const subArr = arr.filter(el =\u003e el.active === true)\n  if (subArr.length \u003e 1) throw new Error('Array should contains only 1 \"active\" element')\n})\n\nSchema.parse([{ active: true, name: 'some 1' }, { active: true, name: 'some 2' }]) // throws Error\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitalics%2Fajv-ts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvitalics%2Fajv-ts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvitalics%2Fajv-ts/lists"}