{"id":48222427,"url":"https://github.com/skunkteam/types","last_synced_at":"2026-04-04T19:12:41.223Z","repository":{"id":38409481,"uuid":"316231062","full_name":"skunkteam/types","owner":"skunkteam","description":"Runtime type-validation with auto-derived TypeScript types","archived":false,"fork":false,"pushed_at":"2025-05-21T14:20:31.000Z","size":1592,"stargazers_count":12,"open_issues_count":3,"forks_count":1,"subscribers_count":7,"default_branch":"next","last_synced_at":"2025-09-29T12:58:18.882Z","etag":null,"topics":["io-ts","runtypes","type-validation","typescript"],"latest_commit_sha":null,"homepage":"","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/skunkteam.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-11-26T12:54:40.000Z","updated_at":"2025-03-24T12:53:14.000Z","dependencies_parsed_at":"2023-09-27T19:31:34.800Z","dependency_job_id":"7fd08463-2d4c-4578-a290-654bbdb3e9c9","html_url":"https://github.com/skunkteam/types","commit_stats":{"total_commits":89,"total_committers":6,"mean_commits":"14.833333333333334","dds":0.1910112359550562,"last_synced_commit":"6532a503ea5ad51234082e9c21f0f4ec796e0d05"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"purl":"pkg:github/skunkteam/types","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skunkteam%2Ftypes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skunkteam%2Ftypes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skunkteam%2Ftypes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skunkteam%2Ftypes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/skunkteam","download_url":"https://codeload.github.com/skunkteam/types/tar.gz/refs/heads/next","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/skunkteam%2Ftypes/sbom","scorecard":{"id":830296,"data":{"date":"2025-08-11","repo":{"name":"github.com/skunkteam/types","commit":"a75d0db13475534d202ed5be65d8eb688185eb22"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":4.7,"checks":[{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":9,"reason":"Found 13/14 approved changesets -- score normalized to 9","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: topLevel 'contents' permission set to 'read': .github/workflows/pr.yml:9","Warn: topLevel 'contents' permission set to 'write': .github/workflows/release.yml:9","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":3,"reason":"dependency not pinned by hash detected -- score normalized to 3","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pr.yml:15: update your workflow using https://app.stepsecurity.io/secureworkflow/skunkteam/types/pr.yml/next?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/pr.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/skunkteam/types/pr.yml/next?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:18: update your workflow using https://app.stepsecurity.io/secureworkflow/skunkteam/types/release.yml/next?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/skunkteam/types/release.yml/next?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   2 out of   2 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Packaging","score":10,"reason":"packaging workflow detected","details":["Info: Project packages its releases by way of GitHub Actions.: .github/workflows/release.yml:14"],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"SAST","score":7,"reason":"SAST tool is not run on all commits -- score normalized to 7","details":["Warn: 23 commits out of 30 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"13 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-h5c3-5r3r-rr8q","Warn: Project is vulnerable to: GHSA-rmvr-2pp2-xj38","Warn: Project is vulnerable to: GHSA-xx4v-prfh-6cgc","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-3mv9-4h5g-vhg3"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-23T17:36:19.915Z","repository_id":38409481,"created_at":"2025-08-23T17:36:19.915Z","updated_at":"2025-08-23T17:36:19.915Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31409550,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["io-ts","runtypes","type-validation","typescript"],"created_at":"2026-04-04T19:12:39.004Z","updated_at":"2026-04-04T19:12:41.207Z","avatar_url":"https://github.com/skunkteam.png","language":"TypeScript","readme":"# Runtime type-validation with auto-derived TypeScript types\n\nInspired by [io-ts](https://github.com/gcanti/io-ts), but without the functional programming (lingo).\n\n-   [Design goals](#design-goals)\n-   [API examples](#api-examples)\n    -   [\"Simple types\"](#\"simple-types\")\n    -   [Object types](#object-types)\n    -   [Validations](#validations)\n        -   [Built-in extra validations](#built-in-extra-validations)\n        -   [Custom validations](#custom-validations)\n    -   [Unions and Intersections](#unions-and-intersections)\n    -   [Generic Types](#generic-types)\n    -   [Parsers](#parsers)\n        -   [List of autoCast parsers](#list-of-autocast-parsers)\n-   [Nest.js integration](#nest.js-integration)\n-   [API reference](markdown/types.md)\n\n## Design goals:\n\n### strict / loose mode\n\nEvery type is strict by default (applies no coercion during validation), but can be converted into a looser variant using the [`autoCast`](markdown/types.autocast.md) feature. Also, objects are stripped of unknown properties by default.\n\n### Great (human-)readable error messages\n\nGreat care has been taken to ensure that (even deeply nested) types emit readable error messages when validation fails.\n\n### No FP API / No FP lingo\n\nWhen integrating in an imperative codebase, using a library that is purely functional programming-oriented is not fun.\n\n### Mimic JavaScript type-constructors\n\nIn JavaScript, one can create a `string` or `number` using the constructors `String` and `Number`. Types created with this library mimic that pattern as you can see in the [API examples](#api-examples) below.\n\n### Branding\n\nTypeScript uses structural typing (to match JavaScript's duck-typing). In short, this means that two interfaces or types that look the same are automatically compatible with each-other, even though they might have different names. This is of course very powerful, for example:\n\n```typescript\ninterface SomeInterface {\n    someProperty: string;\n}\ntype SomeOtherInterface = SomeInterface \u0026 { partial?: 'property' };\nconst obj = { someProperty: 'with a value' };\n\n// Now all three of: `SomeInterface`, `SomeOtherInterface` and `typeof obj` are assignable to each other, because they all satisfy each-other's requirements.\n```\n\nSometimes however, it would be really nice to have nominal types (i.e. types that are different just because they have a different name, even if they look the same). An example from this library is the [`int`](markdown/types.int.md) type. A value that is validated by this type is effectively a `number` that has been validated to be a whole number (an integer). `int`s should be assignable to variables of type `number`, because they are in fact numbers. But not the other way round; a `number` is not guaranteed to be an `int`.\n\nTypeScript currently has limited support for nominal types, but we can work around this limitation with \"branding\". Without going into too much details, this allows us to do the following:\n\n```typescript\n// This is a built-in type, so you don't need to do this yourself, but it's a good\n// example of a branded type. The type `int` defined here is a subtype of number,\n// checked by the `Number.isInteger` function and given the brand `int`.\nconst int = number.withConstraint('int', Number.isInteger);\n\n// This means that all values that are succesfully validated by this type\n// (first checked to be a number and then to satisfy `Number.isInteger`)\n// receive the brand `int`. This is not a runtime thing, but purely a\n// TypeScript design-time aspect of the resulting type.\n\nconst myValue = int(1234);\n\n// At runtime the value is still a number (and TypeScript respects that), so\n// we can do the following:\nconst myCalculatedResult = myValue * 2;\n// And:\nconst veryVeryLongString = '*'.repeat(myValue);\n\n// But TypeScript enforces that we don't accidentally assign or use other kind\n// of numbers where we explicitly only accept integers. Take for example the\n// following function:\nfunction setPageNumber(page: The\u003ctypeof int\u003e): void;\n\n// Wait, what's that awkward type? `The\u003ctypeof int\u003e` is not something we want to\n// write every time, and definitely not something we want to expose to consumers\n// of our APIs. This is why we always combine our type-declaration with a\n// one-liner that creates a TypeScript type with the same name. The following is\n// the actual declaration in this library:\nexport type int = The\u003ctypeof int\u003e;\nexport const int = number.withConstraint('int', Number.isInteger);\n\n// Now we can simply say:\nfunction setPageNumber(page: int): void;\n\n// This is valid usage of this function:\nsetPageNumber(myValue);\n\n// But this isn't, because TypeScript knows we have not validated the given number,\n// so this will fail design-time:\nsetPageNumber(1234);\n\n// It is also possible to sub-type branded types. Example:\ntype uint = The\u003ctypeof uint\u003e;\nconst uint = int.withConstraint('uint', n =\u003e n \u003e= 0);\n\n// Valid:\nconst a: number = uint(123);\nconst b: int = uint(123);\nconst c: uint = uint(123);\n\n// Invalid (TypeScript will complain):\nconst a: uint = 123;\nconst b: uint = int(123);\n```\n\nNote that brands are string-literals, so make sure to use unique brand-names for your types. [io-ts](https://github.com/gcanti/io-ts) uses unique symbols, which have stronger uniqueness guarentees. In this library we opted to use string-literals to allow for a much easier to use API.\n\n### Compatible with TypeScript's `emitDecoratorMetadata` feature\n\nWhen using the `emitDecoratorMetadata` feature of the TypeScript compiler, the compiler will emit some runtime-accessible metadata about all declarations that have decorators. This metadata includes the actual classes that are used as types for parameters or return-types (see https://www.typescriptlang.org/docs/handbook/decorators.html for examples).\n\nIt enables libraries that perform automatic type-validation based on TypeScript typings of a method or constructor. This is done in several frameworks/libraries and can be very convenient. It is limited to classes however, because in TypeScript other types have no runtime aspect. When defining types using this library, types **do** have a runtime aspect. So this library enables the use of any type (even a regexp-validated string, an enum, etc.) as type in a decorated method and makes sure the right metadata is available at runtime for runtime validation. (see [this example of Nest.js integration](#nest.js-integration))\n\nWhen using types in combination with the `emitDecoratorMetadata` feature, make sure to always create a TypeScript type with the same name as the runtime type-validator, as follows:\n\n```typescript\ntype MyType = The\u003ctypeof MyType\u003e;\nconst MyType = // MyType implementation here\n```\n\n## API examples\n\n### \"Simple types\"\n\n```typescript\n/** An example of a simple constraint without a custom message. */\nconst SmallString = string.withConstraint('SmallString', s =\u003e s.length \u003c 10);\n```\n\nThe TypeScript type can be accessed with `The\u003ctypeof SmallString\u003e`, but that is not something we want to write every time, and definitely not something we want to expose to consumers of our APIs. This, and compatibility with decorator metadata, is why we always combine our type-declaration with a one-liner that creates a TypeScript type with the (exact) same name:\n\n```typescript\n/** An example of a simple constraint without a custom message. */\ntype SmallString = The\u003ctypeof SmallString\u003e;\nconst SmallString = string.withConstraint('SmallString', s =\u003e s.length \u003c 10);\n```\n\nTo get a value of that type, simply call the type-constructor:\n\n```typescript\nconst mySmallString = SmallString('123456789');\n// mySmallString has (branded) type: SmallString, value: '123456789'\n\nSmallString('1234567890');\n// throws ValidationError: expected a [SmallString], got: \"1234567890\"\n```\n\nThe error-message is ok, but to get better error messages provide one in your validation-function, for example:\n\n```typescript\n/** A Percentage must be between 0 and 100 inclusive. */\ntype Percentage = The\u003ctypeof Percentage\u003e;\nconst Percentage = number.withConstraint('Percentage', n =\u003e (n \u003e= 0 \u0026\u0026 n \u003c= 100) || 'should be between 0 and 100 inclusive');\n\nPercentage(123);\n// throws ValidationError: error in [Percentage]: should be between 0 and 100 inclusive, got: 123\n```\n\n### Object types\n\nThis is nice and all, but the library really shines once you start combining types into larger structures.\n\n```typescript\n/** User is a basic object type. */\ntype User = The\u003ctypeof User\u003e;\nconst User = object('User', {\n    /** The name of the User, split up into a first- and last-name. */\n    name: object({\n        /** The first name of the User, should not be longer than 9 characters. */\n        first: SmallString,\n        /** The last name, has no restrictions. */\n        last: string,\n    }),\n    /** For reference, we need your shoe size, must be a whole non-negative number. */\n    shoeSize: int.withValidation(n =\u003e n \u003e= 0 || 'reverse running-shoes are not supported yet'),\n});\n\nUser({ shoeSize: -5 });\n// throws ValidationError: errors in [User]:\n//\n// - missing property \u003cname\u003e [{ first: SmallString, last: string }], got: { shoeSize: -5 }\n//\n// - at \u003cshoeSize\u003e: reverse running-shoes are not supported yet, got: -5\n\nUser({ name: { first: \"my name is so incredibly long, you wouldn't believe it\" }, shoeSize: -4 });\n// throws ValidationError: errors in [User]:\n//\n// - at \u003cname\u003e: missing property \u003clast\u003e [string], got: { first: \"my name is so  .. n't believe it\" }\n//\n// - at \u003cshoeSize\u003e: reverse running-shoes are not supported yet, got: -4\n//\n// - at \u003cname.first\u003e: expected a [SmallString], got: \"my name is so incred ..  wouldn't believe it\"\n\nUser({ name: { first: 'Donald', last: 'Duck' }, shoeSize: 1 }); // OK\n```\n\n#### Optional fields\n\nOptional fields can be added with [`withOptional()`](markdown/types.interfacetype.withoptional.md).\n\n```typescript\ntype Name = The\u003ctypeof Name\u003e;\nconst Name = object('Name', {\n    /** First name */\n    first: string,\n    /** Last name */\n    last: string,\n}).withOptional({\n    /** Optional middle name */\n    middle: string,\n});\n\nName({ first: 1 });\n// throws ValidationError: errors in [Name]:\n//\n// - missing property \u003clast\u003e [string], got: { first: 1 }\n//\n// - at \u003cfirst\u003e: expected a string, got a number (1)\n```\n\nNote that `Name` does not complain about a missing `middle` property (because that property is optional).\n\nBy default, `object` validators strip unknown properties. In a future version, this behavior will be configurable.\n\n```typescript\nName({ first: 'first', last: 'last', middle: 'middle', title: 'title' });\n// =\u003e { first: 'first', last: 'last', middle: 'middle' }\n```\n\nNote that, by default, `undefined` values and omitted fields are interchangeable:\n\n```typescript\n// `or` defines a simple union which is explained below\nobject({ prop: string.or(undefinedType) }).is({}); // =\u003e true\n// `partial` is the same as `object`, but all properties are optional\npartial({ prop: string }).is({ prop: undefined }); // =\u003e true\n```\n\nTo opt out of this behavior, use the optional options parameter (first):\n\n```typescript\nobject({ strictMissingKeys: true }, { prop: string.or(undefinedType) }).construct({});\n// throws ValidationError: error in [{ prop: string | undefined }]: missing property \u003cprop\u003e [string | undefined], got: {}\nobject({ strictMissingKeys: true }, { prop: string.or(undefinedType) }).construct({ prop: undefined });\n// =\u003e { prop: undefined }\n```\n\n#### Default values\n\nIt is possible to provide default values for properties that are missing during parsing. This only works for required properties.\n\n```typescript\ntype ObjectWithDefaultValues = The\u003ctypeof ObjectWithDefaultValues\u003e;\nconst ObjectWithDefaultValues = object('ObjectWithDefaultValues', {\n    requiredProp: string.withDefault('this will work'),\n}).withOptional({\n    optionalProp: string.withDefault(\"this doesn't make sense\"),\n});\n\n// Now missing properties are automatically converted to the given default value.\nObjectWithDefaultValues({}); // =\u003e { requiredProp: 'this will work' }\nObjectWithDefaultValues.is({})); // =\u003e false\nObjectWithDefaultValues.is({ requiredProp: 'still required' })); // =\u003e true\n```\n\n### Validations\n\n#### Built-in extra validations\n\nSome built-in types provide a number of common extra validations using the [`withConfig`](markdown/types.basetypeimpl.withconfig.md) method. These declarative extra validations are fully supported by the error-reporter and provide nice error messages on error. These error-messages are customizable in most cases.\n\nAnother advantage of using the `withConfig` method is that the declarative config can be retrieved at runtime by type-analysis tools, such as an OpenAPI / JSON schema generator.\n\n| Type         | Config option                                       | Docs                                                     |\n| :----------- | :-------------------------------------------------- | :------------------------------------------------------- |\n| `array(...)` | `minLength` and `maxLength`                         | [`ArrayTypeConfig`](markdown/types.arraytypeconfig.md)   |\n| `number`     | `max[Exclusive]`, `min[Exclusive]` and `multipleOf` | [`NumberTypeConfig`](markdown/types.numbertypeconfig.md) |\n| `string`     | `minLength`, `maxLength` and `pattern`              | [`StringTypeConfig`](markdown/types.stringtypeconfig.md) |\n\n#### Custom validations\n\nSome validations are more complex than the built-in basic validations and need custom logic. To add custom validation to any type, use the methods [`withValidation`](markdown/types.basetypeimpl.withvalidation.md) (unbranded) and [`withConstraint`](markdown/types.basetypeimpl.withconstraint.md) (branded). The accepted return-value of the validation-callback ([`ValidationResult`](markdown/types.validationresult.md)) is very convenient for both simple custom validations and more complex scenario's.\n\n| Return type                  | Interpretation                                              | Example / usage                          |\n| :--------------------------- | :---------------------------------------------------------- | :--------------------------------------- |\n| `false`                      | Validation **not ok**, no custom message                    | `value === 'ok'`                         |\n| `true`                       | Validation **ok**, no error                                 | `value === 'ok'`                         |\n| `'a string'`                 | Validation **not ok**, provided string is custom message    | `value === 'ok' \\|\\| 'why it is not ok'` |\n| `[]`                         | Validation **ok**, no error                                 | Explained below                          |\n| `['a string', ...]`          | Validation **not ok**, provided strings are custom messages | Explained below                          |\n| `{ kind: '...', ...}`        | Validation **not ok**, provides structured error details    | Explained below                          |\n| `[{ kind: '...', ...}, ...]` | Validation **not ok**, provides structured error details    | Explained below                          |\n\n**Returning multiple violations:**\n\nA common pattern is building up a messages array and returning that, like this:\n\n```ts\nconst messages: string[] = [];\nif (...) messages.push('custom message');\nif (...) messages.push('other custom message');\nreturn messages;\n```\n\nIn this case an empty `messages` array means that no errors occurred. That is why an empty array is regarded as a successful validation.\n\nIt is also possible to use a generator function to yield zero or more errors, for example:\n\n```ts\nnumber.withValidation(function* (n) {\n    if (n \u003c= 10) yield 'should be more than 10';\n    if (n \u003c= 5) yield 'not even close';\n});\n```\n\n**Returning structured error details:**\n\nStructured error details ([`MessageDetails`](markdown/types.messagedetails.md) as used internally by the library) have the advantage that they can provide hints to the error reporter on how to report the errors. This can result in better understandable error messages. For example, when certain properties are only required in certain conditions, return a `MessageDetails` object with kind `'missing property'`.\n\nSuppose you have an API that updates salaries for employees, and a special approval is needed for salaries over $200,000, these are some of the options:\n\n```typescript\n/** The basic request type, validations will be added later */\ntype UpdateSalaryRequest = The\u003ctypeof UpdateSalaryRequest\u003e;\nconst UpdateSalaryRequest = object('UpdateSalaryRequest', {\n    id: string,\n    salary: number,\n}).withOptional({\n    salaryApproval: string,\n});\n\n//\n// The simplest of solutions, only a condition.\n//\ntype SuperBasicValidation = The\u003ctypeof SuperBasicValidation\u003e;\nconst SuperBasicValidation = UpdateSalaryRequest.withValidation(request =\u003e request.salary \u003c 200_000 || !!request.salaryApproval);\n\nSuperBasicValidation({ id: 'emp01', salary: 300_000 });\n// throws ValidationError: error in [UpdateSalaryRequest]: additional validation failed, got: { id: \"emp01\", salary: 300000 }\n\n//\n// Slightly better, at least provide a name to your type:\n//\ntype ValidatedUpdateSalaryRequest = The\u003ctypeof ValidatedUpdateSalaryRequest\u003e;\nconst ValidatedUpdateSalaryRequest = UpdateSalaryRequest.withConstraint(\n    'ValidatedUpdateSalaryRequest',\n    request =\u003e request.salary \u003c 200_000 || !!request.salaryApproval,\n);\n\nValidatedUpdateSalaryRequest({ id: 'emp01', salary: 300_000 });\n// throws ValidationError: expected a [ValidatedUpdateSalaryRequest], got: { id: \"emp01\", salary: 300000 }\n\n//\n// A better solutions, only a custom message. This is often enough.\n//\ntype WithBasicCustomMessage = The\u003ctypeof WithBasicCustomMessage\u003e;\nconst WithBasicCustomMessage = UpdateSalaryRequest.withValidation(\n    request =\u003e request.salary \u003c 200_000 || !!request.salaryApproval || 'approval is needed',\n);\n\nWithBasicCustomMessage({ id: 'emp01', salary: 300_000 });\n// throws ValidationError: error in [UpdateSalaryRequest]: approval is needed, got: { id: \"emp01\", salary: 300000 }\n\n//\n// Report the error with the correct property to enable better reporting of multiple errors, maybe you want this:\n//\ntype WithReportHint = The\u003ctypeof WithReportHint\u003e;\nconst WithReportHint = UpdateSalaryRequest.withValidation(\n    request =\u003e\n        request.salary \u003c 200_000 ||\n        !!request.salaryApproval || [\n            {\n                kind: 'custom message',\n                path: ['salary'],\n                message: 'large salaries are only allowed when approved by the boss',\n                input: request.salary,\n            },\n            {\n                kind: 'custom message',\n                path: ['salaryApproval'],\n                message: 'missing approval for large salaries',\n                input: request.salaryApproval,\n            },\n        ],\n);\n\nWithReportHint({ id: 'emp01', salary: 300_000, salaryApproval: '' });\n// throws ValidationError: errors in [UpdateSalaryRequest]:\n//\n// - at \u003csalary\u003e: large salaries are only allowed when approved by the boss, got: 300000\n//\n// - at \u003csalaryApproval\u003e: missing approval for large salaries, got: \"\"\n\n//\n// Or report a missing properties like this:\n//\ntype ReportMissingProperties = The\u003ctypeof ReportMissingProperties\u003e;\nconst ReportMissingProperties = UpdateSalaryRequest.withValidation(\n    request =\u003e\n        request.salary \u003c 200_000 ||\n        !!request.salaryApproval || {\n            kind: 'missing property',\n            property: 'salaryApproval',\n            type: string,\n        },\n);\n\nWithReportHint({ id: 'emp01', salary: 300_000 });\n// throws error in [UpdateSalaryRequest]: missing property \u003csalaryApproval\u003e [string], got: { id: \"emp01\", salary: 300000 }\n```\n\n### Unions and Intersections\n\nUse [`union()`](markdown/types.union.md) and [`intersection()`](markdown/types.intersection.md) to create unions and intersections. When creating unions or intersections of two types, the methods: [`or()`](markdown/types.basetypeimpl.or.md) and [`and()`](markdown/types.baseobjectliketypeimpl.and.md) might be preferable.\n\n```typescript\n// Example adapted from: https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html#unions-with-common-fields\ntype NetworkState = The\u003ctypeof NetworkState\u003e;\nconst NetworkState = union('NetworkState', [\n    object('NetworkLoadingState', { state: literal('loading') }),\n    object('NetworkFailedState', { state: literal('failed'), code: number }),\n    object('NetworkSuccessState', { state: literal('success'), response: Response }),\n]);\n```\n\nWhen reporting errors, in case of unions, the library tries to be as helpful as possible. Of course all errors are grouped by union-element for better understandability:\n\n```typescript\nNetworkState({});\n// throws ValidationError: error in [NetworkState]: failed every element in union:\n// (got: {})\n//   • error in [NetworkLoadingState]: missing property \u003cstate\u003e [\"loading\"]\n//   • errors in [NetworkFailedState]:\n//     ‣ missing properties \u003cstate\u003e [\"failed\"] and \u003ccode\u003e [number]\n//   • errors in [NetworkSuccessState]:\n//     ‣ missing properties \u003cstate\u003e [\"success\"] and \u003cresponse\u003e [Response]\n```\n\nBut whenever possible, the validation-messages will be limited to the (most likely) intended union-element:\n\n```typescript\nNetworkState({ state: 'failed', code: '500' });\n// throws ValidationError: error in [NetworkState]: in union element [NetworkFailedState] at \u003ccode\u003e: expected a number, got a string (\"500\")\n//   • disregarded 2 union-subtypes due to a mismatch in values of discriminator \u003cstate\u003e\n\n// Or based on the type of value:\nunion([string, boolean, object({ value: number, unit: string })]).check(123);\n// throws ValidationError: error in [string | boolean | { value: number, unit: string }]: expected a boolean, an object or a string, got a number (123)\n\nunion([string, boolean, object({ value: number, unit: string })]).check({});\n// throws ValidationError: error in [string | boolean | { value: number, unit: string }]:\n//   • missing properties \u003cvalue\u003e [number] and \u003cunit\u003e [string], got: {}\n//   • disregarded 2 union-subtypes that do not accept an object\n```\n\n### Generic Types\n\nGeneric types can be modelled as functions. This is best explained with an example.\n\nTo model the following type:\n\n```typescript\ninterface MyGenericWrapper\u003cT\u003e {\n    // Example of an ordinary interface member:\n    ok: boolean;\n    // This is the generic part:\n    inner: T;\n}\n```\n\nCreate the following function:\n\n```typescript\nfunction MyGenericWrapper\u003cT\u003e(innerType: Type\u003cT\u003e) {\n    // The name (first parameter) is of course optional, but it can make life easier when things get more complex.\n    return object(`MyGenericWrapper\u003c${innerType.name}\u003e`, {\n        ok: boolean,\n        inner: innerType,\n    });\n}\n```\n\nAn alias in TypeScript...\n\n```typescript\ntype WrappedUser = MyGenericWrapper\u003cUser\u003e;\n```\n\n... becomes a variable (with the necessary boilerplate) using this library:\n\n```typescript\ntype WrappedUser = The\u003ctypeof WrappedUser\u003e;\nconst WrappedUser = MyGenericWrapper(User);\n```\n\nBut you can also use it inline inside other combinators.\n\n```typescript\ntype UserRequest = The\u003ctypeof UserRequest\u003e;\nconst UserRequest = object('UserRequest', {\n    method: Method,\n    url: Url,\n    body: MyGenericWrapper(User),\n});\n```\n\nNote that you cannot extract a generic type out of the generic function, i.e. the following does not work:\n\n```typescript\n// This is not possible because it is not possible to reason about generic functions in TypeScript types:\ntype GenericWrapper = The\u003ctypeof GenericWrapper\u003e;\nfunction GenericWrapper\u003cT\u003e(inner: Type\u003cT\u003e) {\n    return object({ ok: boolean, inner });\n}\n```\n\nIf you want to have both the generic `GenericWrapper` TypeScript-type and validator, you have to define them both separately. It is not possible to have TypeScript infer the TypeScript-type for you, but you _can_ ask TypeScript to validate the compatibility between the type and the validator, like so:\n\n```typescript\n// Do not use `interface` here, because TypeScript will merge an `interface` and `function`\n// of the same name, which would result in a wrong typedef (and TypeScript will complain).\ntype GenericWrapper\u003cT\u003e = { ok: boolean; inner: T };\nfunction GenericWrapper\u003cT\u003e(inner: Type\u003cT\u003e): ObjectType\u003cGenericWrapper\u003cT\u003e\u003e {\n    return object({ ok: boolean, inner });\n}\n```\n\nWhen intersecting with the provided generic type-parameter, you may have to use the (exported) `Writable` type, as seen in [#25](https://github.com/skunkteam/types/issues/25):\n\n```typescript\ntype AugmentedGeneric\u003cT\u003e = T \u0026 { id: string };\n//                                            Note the use of Writable here =\u003e \\vvvvvvvv/\nfunction AugmentedGeneric\u003cT\u003e(inner: ObjectType\u003cT\u003e): ObjectType\u003cAugmentedGeneric\u003cWritable\u003cT\u003e\u003e\u003e {\n    return intersection([inner, object({ id: string })]);\n}\n```\n\n### Parsers\n\nValidation is most likely used to validate incoming data / messages. Sometimes this data looks a lot like your internal type, but is slightly off. For example, maybe, the input has strings instead of numbers, or a \"yes\"/\"no\" instead of booleans. In those cases you can \"prepend\" a parsing step to your validator. For builtin types the most common conversions are available using the [`autoCast`](markdown/types.autocast.md) feature.\n\nTake the following (questionable) definition of `Age`:\n\n```typescript\ntype Age = The\u003ctypeof Age\u003e;\nconst Age = int.withConstraint('Age', n =\u003e (n \u003e= 0 \u0026\u0026 n \u003c 200) || 'unexpected age');\n\nAge(123); // =\u003e 123\nAge('123');\n// throw ValidationError: error in base type of [Age]: expected a number, got a string (\"123\")\n```\n\nWhen we turn on the `autoCast` feature, it will accept anything it can reasonably (and safely) convert to number:\n\n```typescript\ntype Age = The\u003ctypeof Age\u003e;\nconst Age = autoCast(int.withConstraint('Age', n =\u003e (n \u003e= 0 \u0026\u0026 n \u003c 200) || 'unexpected age'));\n\nAge(123); // =\u003e 123\nAge('123'); // =\u003e 123\n```\n\nThis is why we call the default function the \"type constructor\". It behaves similarly to `Number` and `String`. This is reflected in the API as follows:\n\n```typescript\n// Age(...) is shorthand for Age.construct(...), it uses the optional parser to\n// (try to) construct a valid instance of Age.\nAge.construct('123'); // =\u003e 123\nAge(true);\n// throws ValidationError: error in parser of [AutoCast\u003cAge\u003e]: could not autocast value: true\n\n// `is()` is a type-guard, it returns whether the value is already a valid Age.\nAge.is('123'); // =\u003e false\nAge.is(123); // =\u003e true\n\n// `check()` is the check-only variant of `construct()`, it returns the value, but\n// does not involve the parser\nAge.check('123'); // throws\nAge.check(123); // =\u003e 123\n```\n\nYou can use your own (custom) parser with the [`withParser()`](markdown/types.basetypeimpl.withparser.md). For example:\n\n```typescript\ntype Answer = The\u003ctypeof Answer\u003e;\nconst Answer = boolean.withParser(\n    'Answer',\n    string.andThen(v =\u003e v === 'yes'),\n);\n\nAnswer('yes'); // =\u003e true\nAnswer('no'); // =\u003e false\nAnswer(1);\n// throws ValidationError: error in parser precondition of [Answer]: expected a string, got a number (1)\n\n// Or, as an contrived example, if you want to be more rigid:\ntype Answer = The\u003ctypeof Answer\u003e;\nconst ValidAnswers = keyof({ yes: true, no: false });\nconst Answer = boolean.withParser('Answer', v =\u003e ValidAnswers.translate(v));\n\nAnswer('yes'); // =\u003e true\nAnswer('nope');\n// throws ValidationError: error in parser of [Answer]: expected a [\"yes\" | \"no\"], got: \"nope\"\n```\n\n#### List of autoCast parsers\n\nAll types have an associated `autoCast` type that adds a parser that tries to do some default (convenient) conversions where possible.\n\n| Type                                                | Input                  | Output                                                                                              |\n| --------------------------------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------- |\n| **number**                                          |                        | Numeric autocasts automatically parse strings without known gotchas s.a. `\" \"` and `\"123 abc\"`:     |\n| `number`                                            | `123`                  | `123`                                                                                               |\n| `autoCast(number)`                                  | `123`                  | `123`                                                                                               |\n| `number`                                            | `\"123\"`                | **_`error in [number]: expected a number, got a string (\"123\")`_**                                  |\n| `autoCast(number)`                                  | `\"123\"`                | `123`                                                                                               |\n| `autoCast(number)`                                  | `\" 123 \"`              | `123`                                                                                               |\n| `autoCast(number)`                                  | `\" 123 a\"`             | **_`error in parser of [AutoCast\u003cnumber\u003e]: could not autocast value: \" 123 a\"`_**                   |\n| `autoCast(number)`                                  | `\" \"`                  | **_`error in parser of [AutoCast\u003cnumber\u003e]: could not autocast value: \" \"`_**                        |\n| `autoCast(number)`                                  | `\"Infinity\"`           | `Infinity` (use a checked type like `int` or define a `FiniteNumber` type to prevent this)          |\n| **int**                                             |                        | All numeric types (like `int`) inherit the same autoCast behaviour:                                 |\n| `int`                                               | `123`                  | `123`                                                                                               |\n| `int`                                               | `\"123\"`                | **_`error in [int]: expected a number, got a string (\"123\")`_**                                     |\n| `autoCast(int)`                                     | `\"123\"`                | `123`                                                                                               |\n| `autoCast(int)`                                     | `\"123a\"`               | **_`error in parser of [AutoCast\u003cint\u003e]: could not autocast value: \"123a\"`_**                        |\n| `autoCast(int)`                                     | `123.4`                | **_`error in [AutoCast\u003cint\u003e]: expected a whole number, got: 123.4`_**                               |\n| `autoCast(int)`                                     | `\"123.4\"`              | **_`error in [AutoCast\u003cint\u003e]: expected a whole number, got: 123.4, parsed from: \"123.4\"`_**         |\n| **array**                                           |                        | Arrays can auto-cast non-array values with the following behavior:                                  |\n| `array(number)`                                     | `[1, 2]`               | `[1, 2]`                                                                                            |\n| `autoCast(array(number))`                           | `[1, 2]`               | `[1, 2]`                                                                                            |\n| `array(number)`                                     | `123`                  | **_`error in [number[]]: expected an array, got a number (123)`_**                                  |\n| `autoCast(array(number))`                           | `123`                  | `[123]`                                                                                             |\n| `array(number)`                                     | `undefined`            | **_`error in [number[]]: expected an array, got an undefined `_**                                   |\n| `autoCast(array(number))`                           | `undefined`            | `[]`                                                                                                |\n| `autoCastAll(array(number))`                        | `[123]`                | `[123]` (_autoCastAll_ is a deeply nested _autoCast_, all elements are also _autoCast_)             |\n| `autoCastAll(array(number))`                        | `[\"123\"]`              | `[123]`                                                                                             |\n| `autoCastAll(array(number))`                        | `123`                  | `[123]`                                                                                             |\n| `autoCastAll(array(number))`                        | `\"123\"`                | `[123]`                                                                                             |\n| `unknownArray`                                      | `123`                  | **_`error in [unknown[]]: expected an array, got a number (123)`_**                                 |\n| `autoCast(unknownArray)`                            | `123`                  | `[123]`                                                                                             |\n| `unknownArray`                                      | `undefined`            | **_`error in [unknown[]]: expected an array, got an undefined `_**                                  |\n| `autoCast(unknownArray)`                            | `undefined`            | `[]`                                                                                                |\n| **boolean**                                         |                        | Booleans can autocast from very specific string values (inspired by XML spec)                       |\n| `boolean`                                           | `true`                 | `true`                                                                                              |\n| `boolean`                                           | `false`                | `false`                                                                                             |\n| `boolean`                                           | `\"true\"`               | **_`error in [boolean]: expected a boolean, got a string (\"true\") `_**                              |\n| `autoCast(boolean)`                                 | `\"true\"`               | `true`                                                                                              |\n| `boolean`                                           | `1`                    | **_`error in [boolean]: expected a boolean, got a number (1)`_**                                    |\n| `autoCast(boolean)`                                 | `1`                    | `true`                                                                                              |\n| `autoCast(boolean)`                                 | `\"false\"`              | `false`                                                                                             |\n| `autoCast(boolean)`                                 | `0`                    | `false`                                                                                             |\n| **object** and **partial**                          |                        | Object types have no _autoCast_ variant, but _autoCastAll_ puts all properties into _autoCast_ mode |\n| `object({ a: number })`                             | `{ a: \"1\" }`           | **_`error in [{ a: number }] at \u003ca\u003e: expected a number, got a string (\"1\") `_**                     |\n| `autoCastAll(object({ a: number }))`                | `{ a: \"1\" }`           | `{ a: 1 }`                                                                                          |\n| `object({ a: array(number) })`                      | `{ a: \"1\" }`           | **_`error in [{ a: number[] }]: expected an array, got a string (\"1\")`_**                           |\n| `autoCastAll(object({ a: array(number) }))`         | `{ a: \"1\" }`           | `{ a: [1] }`                                                                                        |\n| `object({ a: array(number) })`                      | `{}`                   | **_`error in [{ a: number[] }]: missing property \u003ca\u003e [number[]], got: {}`_**                        |\n| `autoCastAll(object({ a: array(number) }))`         | `{}`                   | `{ a: [] }` (`object` determines that `AutoCastAll\u003carray(number)\u003e` can handle `undefined` value)    |\n| **keyof** and **valueof**                           |                        | Converts to String before passing to the base type                                                  |\n| `keyof({ false: \"F\", true: \"T\" })`                  | `\"false\"`              | `\"false\"`                                                                                           |\n| `keyof({ false: \"F\", true: \"T\" })`                  | `false`                | **_`error in [\"false\" \\| \"true\"]: expected a string, got a boolean (false)`_**                      |\n| `autoCast(keyof({ false: \"F\", true: \"T\" }))`        | `false`                | `\"false\"`                                                                                           |\n| **literal**                                         |                        | Literals use the autoCast technique that is appropriate for the type of literal                     |\n| `literal(123)`                                      | `\"123\"`                | **_`expected a number (123), got a string (\"123\")`_**                                               |\n| `autoCast(literal(123))`                            | `\"123\"`                | `123`                                                                                               |\n| `literal(\"123\")`                                    | `123`                  | **_`expected a string (\"123\"), got a number (123)`_**                                               |\n| `autoCast(literal(\"123\"))`                          | `123`                  | `\"123\"`                                                                                             |\n| `nullType` (or `literal(null)`)                     | `undefined`            | **_`expected a null, got an undefined`_**                                                           |\n| `autoCast(nullType)` (or `autoCast(literal(null))`) | `undefined`            | `null`                                                                                              |\n| **string**                                          |                        | String can (currently) autoCast everything, this will probably change in the future                 |\n| `string`                                            | `123`                  | **_`error in [string]: expected a string, got a number (123)`_**                                    |\n| `autoCast(string)`                                  | `123`                  | `\"123\"`                                                                                             |\n| `autoCast(string)`                                  | `123n`                 | `\"123\"`                                                                                             |\n| `autoCast(string)`                                  | `false`                | \"false\"                                                                                             |\n| `string`                                            | `null`                 | **_`error in [string]: expected a string, got a null `_**                                           |\n| `autoCast(string)`                                  | `null`                 | **_`error in parser of [AutoCast\u003cstring\u003e]: could not autocast value: null`_**                       |\n| `string`                                            | `undefined`            | **_`error in [string]: expected a string, got an undefined `_**                                     |\n| `autoCast(string)`                                  | `undefined`            | **_`error in parser of [AutoCast\u003cstring\u003e]: could not autocast value: undefined`_**                  |\n| `string`                                            | `Symbol.iterator`      | **_`error in [string]: expected a string, got a symbol ([Symbol: Symbol.iterator])`_**              |\n| `autoCast(string)`                                  | `Symbol.iterator`      | **_`error in parser of [AutoCast\u003cstring\u003e]: could not autocast value: [Symbol: Symbol.iterator]`_**  |\n| `autoCast(string)`                                  | `{ prop: \"value\" }`    | **_`error in parser of [AutoCast\u003cstring\u003e]: could not autocast value: { prop: \"value\" }`_**          |\n| `autoCast(string)`                                  | `function myFunc() {}` | **_`error in parser of [AutoCast\u003cstring\u003e]: could not autocast value: [Function: myFunc]`_**         |\n\n### Literals\n\nIt is quite common to construct (large) object literals in code or in unit tests. Since the type-constructors accepts\n`unknown` as an argument, you run the risk of losing code completion. However, each type also has a\n[`.literal`](markdown/types.basetypeimpl.literal.md) feature\n\n```typescript\nconst NonEmptyString = string.withConstraint('NonEmptyString', s =\u003e !!s.length);\ntype User = The\u003ctypeof User\u003e;\nconst User = object('User', {\n    name: object({\n        first: NonEmptyString,\n        last: NonEmptyString,\n    }),\n    shoeSize: int,\n});\nconst user = User({\n    // this would not get code completion as the functions accepts `unknown` :-(\n});\nconst fullUser: User = {\n    // gets code completion, but requires you to type guard every literal :-(\n    name: {\n        first: NonEmptyString('John'),\n        last: NonEmptyString('Doe'),\n    },\n    shoeSize: int(48),\n};\nconst literalUser = User.literal({\n    // code completion and no need for guarding simple literals :-)\n    name: {\n        first: 'John',\n        last: 'Doe',\n    },\n    shoeSize: 48,\n});\n// constructing a simple object with the same code completion support using DeepUnbranded utility\nconst simpleUser: DeepUnbranded\u003cUser\u003e = {\n    name: {\n        first: 'John',\n        last: 'Doe',\n    },\n    shoeSize: 48,\n};\n```\n\n## Nest.js integration\n\nOne of the frameworks that provides features for runtime validation based on the TypeScript types of parameters is [Nest.js](https://nestjs.com/). The following is an example of integration with Nest.js using a generic type-validation pipe:\n\n```typescript\nimport { ArgumentMetadata, BadRequestException, Injectable, PipeTransform } from '@nestjs/common';\nimport { boolean, isType, reportError, string, Type } from '@skunkteam/types';\n\n// This pipe performs runtime type-validation, you should register\n// it globally, see Nest.js documentation for more details.\n@Injectable()\nexport class TypeValidationPipe implements PipeTransform {\n    transform(value: unknown, { metatype }: ArgumentMetadata) {\n        if (!isType(metatype)) {\n            // You may want to warn or error, instead of skipping\n            // validation, that is up to you.\n            return value;\n        }\n\n        const result = metatype.validate(value, { mode: 'construct' });\n        if (result.ok) {\n            return result.value;\n        }\n        throw new BadRequestException(reportError(result));\n    }\n}\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskunkteam%2Ftypes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fskunkteam%2Ftypes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fskunkteam%2Ftypes/lists"}