{"id":14155563,"url":"https://github.com/grantila/suretype","last_synced_at":"2025-05-15T08:02:33.650Z","repository":{"id":44568429,"uuid":"239636158","full_name":"grantila/suretype","owner":"grantila","description":"Typesafe JSON (Schema) validator","archived":false,"fork":false,"pushed_at":"2023-07-20T05:58:17.000Z","size":1886,"stargazers_count":512,"open_issues_count":17,"forks_count":8,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-08T13:44:32.953Z","etag":null,"topics":["json","json-schema","typescript","validator"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/grantila.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-02-10T23:36:50.000Z","updated_at":"2025-03-18T22:54:04.000Z","dependencies_parsed_at":"2024-01-29T06:01:13.949Z","dependency_job_id":"dfec658c-77b1-4fdc-b322-a913be2fd4a7","html_url":"https://github.com/grantila/suretype","commit_stats":{"total_commits":46,"total_committers":2,"mean_commits":23.0,"dds":"0.021739130434782594","last_synced_commit":"ed1d35ba591c2f23a9a2fd129b05d33ce04f5745"},"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grantila%2Fsuretype","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grantila%2Fsuretype/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grantila%2Fsuretype/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/grantila%2Fsuretype/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/grantila","download_url":"https://codeload.github.com/grantila/suretype/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254301420,"owners_count":22047901,"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":["json","json-schema","typescript","validator"],"created_at":"2024-08-17T08:03:56.322Z","updated_at":"2025-05-15T08:02:33.605Z","avatar_url":"https://github.com/grantila.png","language":"TypeScript","funding_links":[],"categories":["typescript"],"sub_categories":[],"readme":"[![npm version][npm-image]][npm-url]\n[![downloads][downloads-image]][npm-url]\n[![build status][build-image]][build-url]\n[![coverage status][coverage-image]][coverage-url]\n[![Node.JS version][node-version]][node-url]\n\n\n\u003cimg src=\"https://raw.githubusercontent.com/grantila/suretype/master/.github/images/logo.svg\" width=\"100%\" /\u003e\n\nSuretype is a JSON validator targeting TypeScript and JSON Schema. It is **ridiculously type safe** when used in TypeScript, which is good for accuraccy, but also for aiding IDE auto-complete.\n\nIt's as easy as Joi, but ~70x faster.\n\nIt's (at least) as typesafe as Superstruct, but ~100x faster. ~2500x faster than Zod and ~1600x faster than ow.\n\n*These are x (**times**) not %*\n\n\u003cdetails style=\"padding-left: 32px; border-left: 4px solid gray;\"\u003e\n\u003csummary\u003eBenchmark results.\u003c/summary\u003e\n\u003cp\u003e\n\n```\n❯ yarn benchmark\nJoi x 123,593 ops/sec ±0.60% (94 runs sampled)\nSuperstruct x 87,898 ops/sec ±0.33% (92 runs sampled)\nZod x 3,498 ops/sec ±1.15% (91 runs sampled)\now x 5,533 ops/sec ±0.93% (85 runs sampled)\nSureType x 8,982,429 ops/sec ±0.53% (91 runs sampled)\n-----\n73x faster than Joi\n102x faster than Superstruct\n2568x faster than Zod\n1623x faster than ow\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\nIt supports most (if not all) of JSON schema, and *nothing beyond that*, so that the validator schemas written in TypeScript (or JavaScript) can be ensured to be convertible into JSON schema. This also prevents suretype from becoming feature bloated - it has a small and extremely simple API.\n\nErrors are prettified using [awesome-ajv-errors][awesome-ajv-errors-url].\n\nFrom a validator schema defined with suretype, you can trivially:\n\n * Compile a validator function (using the **very** fast [Ajv](https://www.npmjs.com/package/ajv))\n * Extract the corresponding JSON Schema\n * Deduce a TypeScript type corresponding to the validator schema (at compile-time!)\n * Using [typeconv](https://github.com/grantila/typeconv):\n   * [Export](#exporting-using-typeconv) (convert) the validator schema into JSON Schema, Open API, TypeScript types or GraphQL, or;\n   * The opposite (!); convert JSON Schema, Open API, TypeScript types or GraphQL **into** suretype validators! 🎉\n\nThe above makes it ideal in TypeScript environments. When used in RESTful applications, the exported schema can be used to document the APIs using OpenAPI. When used in libraries / clients, the TypeScript interfaces can be extracted to well-documented standalone files (including JSDoc comments).\n\n\n## Versions\n\n * Since version 3;\n   * This is a [pure ESM][pure-esm] package. It requires at least Node 14.13.1, and cannot be used from CommonJS.\n   * This package **can** be used in browsers without special hacks. It will not pretty-print codeframes or use colors if the bundling setup doesn't support it, but will to try to load support for it.\n   * You can control colorized/stylized output globally or per validator\n\n\n# Minimal example\n\nThe following is a validator schema using suretype:\n\n```ts\nimport { v } from \"suretype\"\n\nconst userSchema = v.object( {\n    firstName: v.string( ).required( ),\n    lastName: v.string( ),\n    age: v.number( ).gte( 21 ),\n} );\n```\n\nThis schema object can be compiled into validator functions, and it can be used to deduce the corresponding TypeScript type:\n\n```ts\nimport type { TypeOf } from \"suretype\"\n\ntype User = TypeOf\u003c typeof userSchema \u003e;\n```\n\nThis type is compile-time constructed (or *deduced*), and is semantically identical to:\n\n```ts\ninterface User {\n    firstName: string;\n    lastName?: string;\n    age?: number;\n}\n```\n\nNote the `?` for the optional properties, i.e. those that aren't followed by `required()`.\n\nThere are three ways of compiling a validator function; choose the one that best fits your application and situation. Given:\n\n```ts\nimport { compile } from \"suretype\"\n\nconst data = ... // get data from somewhere, e.g. as a TypeScript unknown\n```\n\n\n## Standard validator\n\nThe default behaviour of `compile` is to return a validator function returning extended Ajv output.\n\n```ts\nconst userValidator = compile( userSchema );\nuserValidator( data );\n// { ok: true } or\n// { ok: false, errors: [Ajv errors...], explanation: string }\n```\n\nThe `explanation` is a pretty-printed error.\n\n\n## Type-guarded validator\n\nUse the second optional argument to specify `simple` mode. The return is a boolean, *type guarded*.\n\n```ts\nconst isUser = compile( userSchema, { simple: true } );\nisUser( data ); // true | false, usable as guard:\n\n// Realistic usage:\n\nif ( isUser( data ) ) {\n    // Valid TypeScript, \u003cdata\u003e is now typed(!) as the User type above\n    data.firstName;\n} else {\n     // TypeScript compile error(!), \u003cdata\u003e is unknown\n    data.firstName;\n}\n```\n\n\n## Type-ensured validator\n\nSpecify `ensure` mode to get a validator function which returns the ***exact*** same output as the input (referentially equal), but with a deduced type. *This is often the most practical mode*.\n\n```ts\nconst ensureUser = compile( userSchema, { ensure: true } );\nensureUser( data ); // returns data or throws an error if the data isn't valid.\n\n// Realistic usage:\n\nconst user = ensureUser( data );\n// \u003cuser\u003e is ensured to be valid, *and* is of type User (as above)\nuser.firstName; // string\nuser.foo; // TypeScript compile-time error, there is no `foo` in User\n```\n\nOn validation failure, the error thrown will be of the class `ValidationError`, which has both the raw Ajv errors as an `errors` property, and the pretty explanation in the property `explanation`.\n\nNote: The returned ensurer function can optionally take a type parameter as long as it is equal to or compatible with the deduced type. This means that if the type is exported from suretype to decorated TypeScript declaration files (with annotations), those types can be used as a type parameter, and the returned type will be that type. Example:\n\n```ts\nimport type { User } from './generated/user'\nconst user = ensureUser\u003c User \u003e( data );\n// user is now of type User\n```\n\n\n## Validate or ensure without compiling\n\nInstead of creating a validator from `compile`, you can use the shorthands `validate`, `isValid` and `ensure`. They correspond to compiling without options, compiling in simple-mode and in ensure-mode.\n\n```ts\nimport { validate, isValid, ensure } from 'suretype'\n\nconst validation = validate( userSchema, data ); // -\u003e Validation object\nconst isUser = isValid( userSchema, data );      // -\u003e Type-guarded boolean\nconst user = ensure( userSchema, data );         // -\u003e user is data of type userSchema\n```\n\n\n## Raw JSON Schema validator\n\nSometimes it's handy to not describe the validator schema programmatically, but rather use a raw JSON Schema. There will be no type deduction, so the corresponding interface must be provided explicitly. Only use this if you know the JSON Schema maps to the interface! `raw` works just like the `v.*` functions and returns a validator schema. It can also be annotated.\n\n```ts\nimport { raw, compile } from 'suretype'\n\ntype User = ...; // Get this type from somewhere\nconst userSchema = raw\u003c User \u003e( { type: 'object', properties: { /* ... */ } } );\n\n// Compile as usual\nconst ensureUser = compile( userSchema, { ensure: true } );\n```\n\n\n## Configure\n\nYou can configure colorization and styling, instead of relying on support detection.\n\nEither globally:\n```ts\nimport { setSuretypeOptions } from 'suretype'\n\nsetSuretypeOptions( {\n    colors: true | false,\n    location: true | false,\n    bigNumbers: true | false,\n} );\n```\n\nand/or per validator, e.g.:\n```ts\nimport { compile } from 'suretype'\n\nconst ensureThing = compile(\n    schemaThing,\n    { ensure: true, color: true, location: false }\n);\n```\n\n\n# Annotating schemas\n\nYou can annotate a validator schema using `suretype()` or `annotate()`. The return value is still a validator schema, but when exporting it, the annotations will be included.\n\nThe difference between `suretype()` and `annotate()` is that `suretype()` requires the `name` property, where as it's optional in `annotate()`. Use `suretype()` to annotate top-level schemas so that they have proper names in the corresponding JSON Schema.\n\nAnnotations are useful when exporting the schema to other formats (e.g. JSON Schema or pretty TypeScript interfaces).\n\n```ts\nimport { suretype, annotate, v } from \"suretype\"\n\nconst cartItemSchema = suretype(\n    // Annotations\n    { name: \"CartItem\" },\n    // The validator schema\n    v.object( {\n        productId: annotate( { title: \"The product id string\" }, v.string( ) ),\n        // ...\n    } )\n);\n```\n\nThe interface (i.e. the fields you can use) is called `Annotations`:\n\n```ts\ninterface Annotations {\n\tname: string;\n\ttitle?: string;\n\tdescription?: string;\n\texamples?: Array\u003c string \u003e;\n}\n```\n\nwhere only the `name` is required.\n\n\n# Thorough example\n\nThe following are two types, one using (or *depending on*) the other. They are *named*, which will be reflected in the JSON schema, shown below.\n\nThe `userSchema` is the same as in the above example, although it's wrapped in `suretype()` which annotates it with a name and other attributes.\n\n\u003cdetails style=\"padding-left: 32px;border-left: 4px solid gray;\"\u003e\n\u003csummary\u003eGiven these validation schemas:\u003c/summary\u003e\n\u003cp\u003e\n\n```ts\nimport { suretype, v } from \"suretype\"\n\nconst userSchema = suretype(\n    {\n        name: \"V1User\",\n        title: \"User type, version 1\",\n        description: `\n            A User object must have a firstName property,\n            all other properties are optional.\n        `,\n        examples: [\n            {\n                firstName: \"John\",\n                lastName: \"Doe\",\n            }\n        ],\n    },\n    v.object( {\n        firstName: v.string( ).required( ),\n        lastName: v.string( ),\n        age: v.number( ).gte( 21 ),\n    } )\n);\n\nconst messageSchema = suretype(\n    {\n        name: \"V1Message\",\n        title: \"A message from a certain user\",\n    },\n    v.object( {\n        user: userSchema.required( ),\n        line: v.string( ).required( ),\n    } )\n);\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\nThe JSON schema for these can be extracted, either each type by itself:\n\n```ts\nimport { extractSingleJsonSchema } from \"suretype\"\n\n// The JSON schema for User\nconst { schema: jsonSchema } = extractSingleJsonSchema( userSchema );\n```\n\nor as all types at once, into one big JSON schema. In this case, all validation schemas provided **must** be wrapped with `suretype()`, as they will become JSON schema *\"definitions\"* and therefore must have at least a name.\n\n```ts\nimport { extractJsonSchema } from \"suretype\"\n\nconst { schema: jsonSchema, lookup, schemaRefName } =\n    extractJsonSchema( [ userSchema, messageSchema ], { /* opts... */ } );\n```\n\nAn optional second argument can be provided on the form:\n\n```ts\ninterface ExtractJsonSchemaOptions {\n    refMethod?: ExportRefMethod;\n    onTopLevelNameConflict?: OnTopLevelNameConflict;\n    onNonSuretypeValidator?: OnNonSuretypeValidator;\n}\n```\n\nThe `ExportRefMethod` type is a string union defined as:\n```ts\n    | 'no-refs'  // Don't ref anything. Inline all types to monolith types.\n    | 'provided' // Reference types that are explicitly provided.\n    | 'ref-all'  // Ref all provided types and those with names, suretype()'d.\n```\n\nThe `OnTopLevelNameConflict` type is a string union defined as:\n```ts\n    | 'error'  // Fail the operation\n    | 'rename' // Rename the validators to a unique name\n```\n\nThe `OnNonSuretypeValidator` type is a string union defined as:\n```ts\n    | 'error'       // Fail the operation\n    | 'ignore'      // Ignore, don't export\n    | 'create-name' // Create a name 'Unknown'\n    | 'lookup'      // Provide in lookup table\n```\n\nIf `lookup` is specified, it allows unnamed validators. They won't exist in the resulting schema, but in a lookup table next to it. This lookup table will always exist, using this setting will simply allow unnamed validators.\n\nThe result is an object on the form:\n\n```ts\ninterface ExtractedJsonSchema {\n    schema: SchemaWithDefinitions; // Contains a 'definitions' property\n    lookup: Map\u003c CoreValidator\u003c unknown \u003e, any \u003e;\n    schemaRefName: Map\u003c any, string \u003e;\n}\n```\n\nThe `lookup` is useful to lookup the json schema for a certain validator object reference, especially unnamed ones which are not included in the schema.\n\nThe `schemaRefName` contains a lookup map from (top-level) schema object to its name as used when referring to it, not necessarily the same as what it is internally named, if there where naming conflicts and `OnTopLevelNameConflict` is `rename`.\n\nIn the example above, the `jsonSchema` *object* (which can be `JSON.stringify`'d) will be something like:\n\n\u003cdetails style=\"padding-left: 32px;border-left: 4px solid gray;\"\u003e\n\u003csummary\u003eJSON Schema\u003c/summary\u003e\n\u003cp\u003e\n\n```js\n{\n    \"$schema\": \"http://json-schema.org/draft-07/schema#\",\n    \"definitions\": {\n        \"V1User\": { // \u003c-- This corresponds to the \"name\" property in suretype()\n            \"title\": \"User type, version 1\",\n            \"description\": \"A User object must have a firstName property,\\nall other properties are optional.\",\n            \"examples\": [\n                {\n                    \"firstName\": \"John\",\n                    \"lastName\": \"Doe\"\n                }\n            ],\n            \"type\": \"object\",\n            \"properties\": {\n                \"firstName\": { \"type\": \"string\" },\n                \"lastName\": { \"type\": \"string\" },\n                \"age\": { \"type\": \"number\", \"minimum\": 13 }\n            },\n            \"required\": [ \"firstName\" ]\n        },\n        \"V1Message\": {\n            \"title\": \"A message from a certain user\",\n            \"type\": \"object\",\n            \"properties\": {\n                \"user\": { \"$ref\": \"#/definitions/V1User\" }, // \u003c-- Proper references\n                \"line\": { \"type\": \"string\" }\n            },\n            \"required\": [ \"user\", \"line\" ]\n        }\n    }\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\n### Exporting using typeconv\n\n\u003cimg src=\"https://raw.githubusercontent.com/grantila/suretype/master/.github/images/suretype-typeconv.svg\" width=\"25%\" /\u003e\n\nA better (well, often much more practical) way of converting suretype validator schemas into JSON Schema is by using [`typeconv`][typeconv-github-url] [![npm version][typeconv-image]][typeconv-npm-url].\n\nYou can convert from suretype validator schemas to:\n * TypeScript interfaces (pretty printed with JSDoc comments)\n * JSON Schema\n * Open API\n * GraphQL\n\nWhen converting **from** suretype, typeconv will convert all *exported* validator schemas from the source files.\n\nExample *from* SureType *to* TypeScript; `$ npx typeconv -f st -t ts -o generated 'src/validators/**/*.ts'`\n\nYou can also convert **from** any of these formats ***into*** suretype validators!\n\nExample *from* Open API *to* SureType; `$ npx typeconv -f oapi -t st -o generated 'schemas/**/*.yml'`\n\n\n[npm-image]: https://img.shields.io/npm/v/suretype.svg\n[npm-url]: https://npmjs.org/package/suretype\n[downloads-image]: https://img.shields.io/npm/dm/suretype.svg\n[build-image]: https://img.shields.io/github/actions/workflow/status/grantila/suretype/master.yml?branch=master\n[build-url]: https://github.com/grantila/suretype/actions?query=workflow%3AMaster\n[coverage-image]: https://coveralls.io/repos/github/grantila/suretype/badge.svg?branch=master\n[coverage-url]: https://coveralls.io/github/grantila/suretype?branch=master\n[node-version]: https://img.shields.io/node/v/suretype\n[node-url]: https://nodejs.org/en/\n[awesome-ajv-errors-url]: https://github.com/grantila/awesome-ajv-errors\n\n[typeconv-image]: https://img.shields.io/npm/v/typeconv.svg\n[typeconv-github-url]: https://github.com/grantila/typeconv\n[typeconv-npm-url]: https://npmjs.org/package/typeconv\n[pure-esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrantila%2Fsuretype","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgrantila%2Fsuretype","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgrantila%2Fsuretype/lists"}