{"id":13528269,"url":"https://github.com/runtypes/runtypes","last_synced_at":"2025-05-11T03:45:35.552Z","repository":{"id":39873465,"uuid":"59254977","full_name":"runtypes/runtypes","owner":"runtypes","description":"Runtime validation for static types","archived":false,"fork":false,"pushed_at":"2025-02-05T17:44:10.000Z","size":1486,"stargazers_count":2650,"open_issues_count":19,"forks_count":93,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-05-08T00:59:40.622Z","etag":null,"topics":["runtime","types","typescript","validation"],"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/runtypes.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}},"created_at":"2016-05-20T01:24:26.000Z","updated_at":"2025-05-03T06:10:30.000Z","dependencies_parsed_at":"2023-12-06T08:46:53.822Z","dependency_job_id":"73514c13-0c5d-4d30-a682-26c0abee5cbb","html_url":"https://github.com/runtypes/runtypes","commit_stats":{"total_commits":545,"total_committers":44,"mean_commits":"12.386363636363637","dds":0.3302752293577982,"last_synced_commit":"a9a0706368aecacce3f5b7123cff8a73d9d3ede7"},"previous_names":["pelotom/runtypes"],"tags_count":81,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runtypes%2Fruntypes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runtypes%2Fruntypes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runtypes%2Fruntypes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/runtypes%2Fruntypes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/runtypes","download_url":"https://codeload.github.com/runtypes/runtypes/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252978754,"owners_count":21834915,"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":["runtime","types","typescript","validation"],"created_at":"2024-08-01T06:02:22.634Z","updated_at":"2025-05-08T01:00:00.825Z","avatar_url":"https://github.com/runtypes.png","language":"TypeScript","readme":"\u003cdiv align=\"center\"\u003e\u003cbr\u003e\u003cbr\u003e\n\n# Runtypes\n\n[![License](https://img.shields.io/github/license/runtypes/runtypes?color=%231e2327)](LICENSE) [![JSR](https://jsr.io/badges/@runtypes/runtypes)](https://jsr.io/@runtypes/runtypes) [![NPM Version](https://img.shields.io/npm/v/runtypes)](https://www.npmjs.com/package/runtypes) [![Coverage Status](https://coveralls.io/repos/github/runtypes/runtypes/badge.svg?branch=master)](https://coveralls.io/github/runtypes/runtypes?branch=master)\n\nSafely bring untyped data into the fold.\n\n\u003cbr\u003e\u003cbr\u003e\u003c/div\u003e\n\nRuntypes allow you to take values about which you have no assurances and check that they conform to some type `A`. This is done by means of composable type validators of primitives, literals, arrays, tuples, objects, unions, intersections and more.\n\n## Example\n\nSuppose you have objects which represent asteroids, planets, ships and crew members. In TypeScript, you might write their types like so:\n\n```ts\ntype Vector = [number, number, number]\n\ntype Asteroid = {\n\ttype: \"asteroid\"\n\tlocation: Vector\n\tmass: number\n}\n\ntype Planet = {\n\ttype: \"planet\"\n\tlocation: Vector\n\tmass: number\n\tpopulation: number\n\thabitable: boolean\n}\n\ntype Rank = \"captain\" | \"first mate\" | \"officer\" | \"ensign\"\n\ntype CrewMember = {\n\tname: string\n\tage: number\n\trank: Rank\n\thome: Planet\n}\n\ntype Ship = {\n\ttype: \"ship\"\n\tlocation: Vector\n\tmass: number\n\tname: string\n\tcrew: CrewMember[]\n}\n\ntype SpaceObject = Asteroid | Planet | Ship\n```\n\nIf the objects which are supposed to have these shapes are loaded from some external source, perhaps a JSON file, we need to validate that the objects conform to their specifications. We do so by building corresponding `Runtype`s in a very straightforward manner:\n\n```ts\nimport { Boolean, Number, String, Literal, Array, Tuple, Object, Union } from \"runtypes\"\n\nconst Vector = Tuple(Number, Number, Number)\n\nconst Asteroid = Object({\n\ttype: Literal(\"asteroid\"),\n\tlocation: Vector,\n\tmass: Number,\n})\n\nconst Planet = Object({\n\ttype: Literal(\"planet\"),\n\tlocation: Vector,\n\tmass: Number,\n\tpopulation: Number,\n\thabitable: Boolean,\n})\n\nconst Rank = Union(Literal(\"captain\"), Literal(\"first mate\"), Literal(\"officer\"), Literal(\"ensign\"))\n\nconst CrewMember = Object({\n\tname: String,\n\tage: Number,\n\trank: Rank,\n\thome: Planet,\n})\n\nconst Ship = Object({\n\ttype: Literal(\"ship\"),\n\tlocation: Vector,\n\tmass: Number,\n\tname: String,\n\tcrew: Array(CrewMember),\n})\n\nconst SpaceObject = Union(Asteroid, Planet, Ship)\n```\n\n(See the [examples](examples) directory for an expanded version of this.)\n\nNow if we are given a putative `SpaceObject` we can validate it like so:\n\n```ts\n// spaceObject: SpaceObject\nconst spaceObject = SpaceObject.check(value)\n```\n\nIf the object doesn't conform to the type specification, `check` will throw an exception.\n\n## Error information\n\nWhen it fails to validate, your runtype emits a `ValidationError` object that contains detailed information that describes what's the problem. Following properties are available in the object:\n\n- `name`: Always `\"ValidationError\"`\n- `message`: A `string` that summarizes the problem overall\n- `failure`: A [`Failure`](src/result/Failure.ts) that describes the problem in a structured way\n\nIf you want to inspect why the validation failed, look into the `failure` object:\n\n- `code`: A [`Failcode`](src/result/Failcode.ts) that roughly categorizes the problem\n- `message`: A `string` that summarizes the problem overall\n- `expected`: The runtype that yielded this failure\n- `received`: The value that caused this failure\n- `details`: An object that describes which property was invalid precisely. Only available for complex runtypes (e.g. `Object`, `Array`, and the like; `Union` and `Intersect` also emit this enumerating a failure for each member)\n- `detail`: An object that describes the failure of the inner runtype. Only available for `Brand` and contextual failures (e.g. failures in `Record` keys, in boundaries of `Contract`/`AsyncContract`, etc.)\n- `thrown`: A thrown value, which is typically an error message, if any. Only available for runtypes that involve user-provided validation functions (e.g. `Constraint`, `Parser`, and `InstanceOf`) or constraint-like failures like about the length of `Tuple`\n\nWhat shapes of failures there might actually be is documented on the JSDoc comment of each runtype.\n\nIf you want to inform your users about the validation error, it's strongly discouraged to rely on the format of the `message` property, as it may change across minor versions for readability thoughts. Instead of parsing `message`, you should use other properties to handle further tasks such as i18n.\n\n## Static type inference\n\nThe inferred type of `Asteroid` in the above example is a subtype of\n\n```ts\nRuntype\u003c{\n\ttype: \"asteroid\"\n\tlocation: [number, number, number]\n\tmass: number\n}\u003e\n```\n\nThat is, it's a `Runtype\u003cAsteroid\u003e`, and you could annotate it as such. But we don't really have to define the `Asteroid` type at all now, because the inferred type is correct. Defining each of your types twice, once at the type level and then again at the value level, is a pain and not very [DRY](https://en.wikipedia.org/wiki/Don't_repeat_yourself). Fortunately you can define a static `Asteroid` type which is an alias to the `Runtype`-derived type like so:\n\n```ts\ntype Asteroid = Static\u003ctypeof Asteroid\u003e\n```\n\nwhich achieves the same result as\n\n```ts\ntype Asteroid = {\n\ttype: \"asteroid\"\n\tlocation: [number, number, number]\n\tmass: number\n}\n```\n\n## Conforming to predefined static type\n\nInstead of getting it to be inferred, you should be able to create a runtype that corresponds to a static type predefined somewhere. In such case you can statically ensure that your runtype conforms to the specification, by using `.conform\u003cT\u003e()`:\n\n```typescript\ntype Specification = {\n\tfoo: string\n\tbar?: string\n}\nconst Correct = Object({\n\tfoo: String,\n\tbar: String.optional(),\n}).conform\u003cSpecification\u003e()\n// @ts-expect-error: should fail\nconst Wrong = Object({\n\tfoo: String,\n\tbar: String,\n}).conform\u003cSpecification\u003e()\n```\n\nThe error message on the wrong definition might be verbose like below, but you'll eventually find it contains where is the wrong piece if you scroll down the wall of text.\n\n```plaintext\nThe 'this' context of type 'Object\u003c{ foo: String; bar: String; }\u003e' is not assignable to method's 'this' of type 'Conform\u003cSpecification\u003e'.\n\tType 'Object\u003c{ foo: String; bar: String; }\u003e' is not assignable to type 'Conformance\u003cSpecification\u003e'.\n\t\tTypes of property '[RuntypeConformance]' are incompatible.\n\t\t\tType '(StaticTypeOfThis: { foo: string; bar: string; }) =\u003e { foo: string; bar: string; }' is not assignable to type '(StaticTypeOfThis: Specification) =\u003e Specification'.\n\t\t\t\tTypes of parameters 'StaticTypeOfThis' and 'StaticTypeOfThis' are incompatible.\n\t\t\t\t\tType 'Specification' is not assignable to type '{ foo: string; bar: string; }'.\n\t\t\t\t\t\tProperty 'bar' is optional in type 'Specification' but required in type '{ foo: string; bar: string; }'.\n```\n\n## Guard function\n\nRuntypes provide a guard function as the `guard` method:\n\n```ts\nconst disembark = (value: unknown) =\u003e {\n\tif (SpaceObject.guard(value)) {\n\t\t// value: SpaceObject\n\t\tif (value.type === \"ship\") {\n\t\t\t// value: Ship\n\t\t\tvalue.crew = []\n\t\t}\n\t}\n}\n```\n\n## Assertion function\n\nRuntypes provide an assertion function as the `assert` method:\n\n```ts\nconst disembark = (value: unknown) =\u003e {\n\ttry {\n\t\tSpaceObject.assert(value)\n\t\t// value: SpaceObject\n\t\tif (value.type === \"ship\") {\n\t\t\t// value: Ship\n\t\t\tvalue.crew = []\n\t\t}\n\t} catch (error) {}\n}\n```\n\nThis might be uncomfortable that TypeScript requires you to manually write the type annotation for your runtype.\n\n## Constraint checking\n\nBeyond mere type checking, we can add arbitrary runtime constraints to a `Runtype`:\n\n```ts\nconst PositiveNumber = Number.withConstraint(n =\u003e n \u003e 0)\nPositiveNumber.check(-3) // Throws error: Failed constraint check\n```\n\nYou can provide more descriptive error messages for failed constraints by returning a string instead of `false`:\n\n```ts\nconst PositiveNumber = Number.withConstraint(n =\u003e n \u003e 0 || `${n} is not positive`)\nPositiveNumber.check(-3) // Throws error: -3 is not positive\n```\n\n### Narrowing the static type\n\nConstraint checking narrows down the original type to a subtype of it. This should be reflected on the static type. You can pass the desired type as the type argument:\n\n```typescript\nconst TheAnswer = Literal(42)\nconst WithConstraint = Number.withConstraint\u003c42\u003e(TheAnswer.guard)\ntype WithConstraint = Static\u003ctypeof WithConstraint\u003e // 42\n```\n\nAlternatively, you can directly wire up the TypeScript's own facility to narrow down types: guard functions and assertion functions. There're corresponding methods on a runtype, so choose the most concise one:\n\n```typescript\nconst WithGuard = Number.withGuard(TheAnswer.guard)\ntype WithGuard = Static\u003ctypeof WithGuard\u003e // 42\nconst WithAssertion = Number.withAssertion(TheAnswer.assert)\ntype WithAssertion = Static\u003ctypeof WithAssertion\u003e // 42\n```\n\nIf you want to provide custom error messages while narrowing static types, you can throw `string` or `Error` from a constraint, guard, or assertion function. Actually, returning a string from a function passed to `withConstraint` is supported by this exception handling internally.\n\nToo often there might be cases you can't express desired types exactly in TypeScript, such as the type for positive numbers. In such cases you should at least express them as branded types.\n\n```typescript\nconst PositiveNumber = Number.withConstraint(n =\u003e n \u003e 0).withBrand(\"PositiveNumber\")\n```\n\n`withBrand` modifier is also useful when you want to give your runtype a custom name, which will be used in error messages.\n\n## Template literals\n\nThe `Template` runtype validates that a value is a string that conforms to the template.\n\nYou can use the familiar syntax to create a `Template` runtype:\n\n```ts\nconst T = Template`foo${Literal(\"bar\")}baz`\n```\n\nBut then the type inference won't work:\n\n```ts\ntype T = Static\u003ctypeof T\u003e // string\n```\n\nBecause TS doesn't provide the exact string literal type information (`[\"foo\", \"baz\"]` in this case) to the underlying function. See the issue [microsoft/TypeScript#33304](https://github.com/microsoft/TypeScript/issues/33304), especially this comment [microsoft/TypeScript#33304 (comment)](https://github.com/microsoft/TypeScript/issues/33304#issuecomment-697977783) we hope to be implemented.\n\nIf you want the type inference rather than the tagged syntax, you have to manually write a function call:\n\n```ts\nconst T = Template([\"foo\", \"baz\"] as const, Literal(\"bar\"))\ntype T = Static\u003ctypeof T\u003e // \"foobarbaz\"\n```\n\nAs a convenient solution for this, it also supports another style of passing arguments:\n\n```ts\nconst T = Template(\"foo\", Literal(\"bar\"), \"baz\")\ntype T = Static\u003ctypeof T\u003e // \"foobarbaz\"\n```\n\nYou can pass various things to the `Template` constructor, as long as they are assignable to `string | number | bigint | boolean | null | undefined` and the corresponding `Runtype`s:\n\n```ts\n// Equivalent runtypes\nTemplate(Literal(\"42\"))\nTemplate(42)\nTemplate(Template(\"42\"))\nTemplate(4, \"2\")\nTemplate(Literal(4), \"2\")\nTemplate(String.withConstraint(s =\u003e s === \"42\"))\nTemplate(\n\tIntersect(\n\t\tNumber.withConstraint(n =\u003e n === 42),\n\t\tString.withConstraint(s =\u003e s.length === 2),\n\t\t// `Number`s in `Template` accept alternative representations like `\"0x2A\"`,\n\t\t// thus we have to constraint the length of string, to accept only `\"42\"`\n\t),\n)\n```\n\nTrivial items such as bare literals, `Literal`s, and single-element `Union`s and `Intersect`s are all coerced into strings at the creation time of the runtype. Additionally, `Union`s of such runtypes are converted into `RegExp` patterns like `(?:foo|bar|...)`, so we can assume `Union` of `Literal`s is a fully supported runtype in `Template`.\n\n### Caveats\n\nA `Template` internally constructs a `RegExp` to parse strings. This can lead to a problem if it contains multiple non-literal runtypes:\n\n```ts\nconst UpperCaseString = String.withConstraint(s =\u003e s === s.toUpperCase(), {\n\tname: \"UpperCaseString\",\n})\nconst LowerCaseString = String.withConstraint(s =\u003e s === s.toLowerCase(), {\n\tname: \"LowerCaseString\",\n})\nTemplate(UpperCaseString, LowerCaseString) // DON'T DO THIS!\n```\n\nThe only thing we can do for parsing such strings correctly is brute-forcing every single possible combination until it fulfills all the constraints, which must be hardly done. Actually `Template` treats `String` runtypes as the simplest `RegExp` pattern `.*` and the “greedy” strategy is always used, that is, the above runtype won't work expectedly because the entire pattern is just `^(.*)(.*)$` and the first `.*` always wins. You have to avoid using `Constraint` this way, and instead manually parse it using a single `Constraint` which covers the entire string.\n\n## Variadic tuples\n\nYou can spread a `Tuple` or an `Array` within arguments of `Tuple`.\n\n```typescript\nconst T = Tuple(Literal(0), ...Tuple(Literal(1), Literal(2)), Literal(3))\ntype T = Static\u003ctypeof T\u003e // [0, 1, 2, 3]\n\nconst U = Tuple(Literal(0), ...Array(Literal(1)), Literal(2))\ntype U = Static\u003ctypeof U\u003e // [0, ...1[], 2]\n```\n\nComponent runtypes are expectedly inferred like this:\n\n```typescript\nconst T = Tuple(Literal(0), ...Tuple(Literal(1), Literal(2)), Literal(3))\nconst literal0: Literal\u003c0\u003e = T.components[0]\nconst literal1: Literal\u003c1\u003e = T.components[1]\nconst literal2: Literal\u003c2\u003e = T.components[2]\nconst literal3: Literal\u003c3\u003e = T.components[3]\n```\n\nNested spreading is also supported:\n\n```typescript\nconst T = Tuple(Literal(0), ...Tuple(Literal(1), ...Array(Literal(2)), Literal(3)), Literal(4))\nconst leading0: Literal\u003c0\u003e = T.components.leading[0]\nconst leading1: Literal\u003c1\u003e = T.components.leading[1]\nconst rest: Array\u003cLiteral\u003c2\u003e\u003e = T.components.rest\nconst trailing0: Literal\u003c3\u003e = T.components.trailing[0]\nconst trailing1: Literal\u003c4\u003e = T.components.trailing[1]\n```\n\n## `instanceof` wrapper\n\nIf you have access to the class that you want to test values with the `instanceof` operator, then the `InstanceOf` runtype is exactly what you're looking for. Usage is straightforward:\n\n```ts\nclass ObjectId { ... };\nconst ObjectIdChecker = InstanceOf(ObjectId);\nObjectIdChecker.check(value);\n```\n\n## Branded types\n\nBranded types is a way to emphasize the uniqueness of a type. This is useful [until we have nominal types](https://github.com/microsoft/TypeScript/pull/33038):\n\n```ts\nconst Username = String.withBrand(\"Username\")\nconst Password = String.withBrand(\"Password\").withConstraint(\n\tstr =\u003e str.length \u003e= 8 || \"Too short password\",\n)\n\nconst signIn = Contract({\n\treceives: Tuple(Username, Password),\n\treturns: Unknown,\n}).enforce((username, password) =\u003e {\n\t/*...*/\n})\n\nconst username = Username.check(\"someone@example.com\")\nconst password = Password.check(\"12345678\")\n\n// Static type OK, runtime OK\nsignIn(username, password)\n\n// Static type ERROR, runtime OK\nsignIn(password, username)\n\n// Static type ERROR, runtime OK\nsignIn(\"someone@example.com\", \"12345678\")\n```\n\n## Optional properties\n\n`Object` runtypes should be able to express optional properties. There's a modifier to do that: `.optional()`.\n\n```ts\nObject({ x: Number.optional() })\n```\n\nYou must be aware of the difference between `Object({ x: Union(String, Undefined) })` and `Object({ x: String.optional() })`; the former means “_**`x` must be present**, and must be `string` or `undefined`_”, while the latter means “_**`x` can be present or absent**, but must be `string` if present_”.\n\nIt's strongly discouraged to disable [`\"exactOptionalPropertyTypes\"`](https://www.typescriptlang.org/tsconfig/#exactOptionalPropertyTypes) in the tsconfig; if you do so, the correspondence between runtypes and the inferred static types get lost. We can't respect tsconfig at runtime, so `runtypes` always conform the behavior `\"exactOptionalPropertyTypes\": true`, in favor of the expressiveness.\n\n## Exact object validation\n\n`Object` has a modifier to perform exact object validation: `.exact()`.\n\n```typescript\nconst O = Object({ x: Number }).exact()\nO.guard({ x: 42 }) // true\nO.guard({ x: 42, y: 24 }) // false\n```\n\nNote that [TypeScript doesn't have exact types](https://github.com/microsoft/TypeScript/issues/12936) at the moment, so it's recommended to wrap your exact `Object` runtype within a `Brand` to at least prevent the unexpected behavior of the inferred static type:\n\n```typescript\nconst x0 = { x: 42 }\nconst x1 = { x: 42, y: 24 }\n\nconst O = Object({ x: Number }).exact()\ntype O = Static\u003ctypeof O\u003e\nconst o0: O = x0\nconst o1: O = x1 // You would not want this to be possible.\nglobalThis.Object.hasOwn(o1, \"y\") === true\n\nconst P = O.withBrand(\"P\")\ntype P = Static\u003ctypeof P\u003e\nconst p0: P = P.check(x0) // Branded types require explicit assertion.\nconst p1: P = P.check(x1) // So this won't accidentally pass at runtime.\n```\n\nYou should beware that `Object` validation only respects **enumerable own** keys; thus if you want to completely eliminate extra properties that may be non-enumerable or inherited, use `parse` method.\n\n## Parsing after validation\n\nEvery runtype has the `withParser` and `parse` methods that offer the functionality to transform validated values automatically.\n\n```typescript\nconst O = Object({ x: String.withParser(parseInt).default(42) })\ntype OStatic = Static\u003ctypeof O\u003e // { x: string }\ntype OParsed = Parsed\u003ctypeof O\u003e // { x: number }\nO.parse({ x: \"42\" }).x === 42\n```\n\nThe `.default(...)` modifier works the same as `.optional()` for mere validation, but for parsing, it works as falling back to the value if the property was absent.\n\n```typescript\nO.parse({}).x === 42\n```\n\nExtraneous properties are not copied to the resulting value.\n\n```typescript\n\"y\" in O.parse({ y: \"extra\" }) === false\n```\n\nWhile `parse` returns a new value, traditional validation methods such as `check` don't change their semantics even with parsers.\n\n```typescript\nconst o: OStatic = { x: \"42\" }\no === O.check(o)\n```\n\n### Semantics in complex runtypes\n\nIn an `Object`, an `Array`, and a `Tuple`, `Parser`s will work just as you'd expect.\n\nIn a `Template`, parsing can work like this:\n\n```typescript\nconst TrueToFalse = Literal(\"true\").withParser(() =\u003e \"false\" as const)\nconst Value = Template(\"value: \", TrueToFalse)\nValue.parse(\"value: true\") === \"value: false\"\n```\n\nIn a `Union`, the first succeeding runtype returns a value and further alternatives are not executed at all:\n\n```typescript\nconst Flip = Union(\n\tBoolean.withParser(b =\u003e !b),\n\tBoolean.withParser(b =\u003e !!b),\n)\nFlip.parse(true) === false\n```\n\nIn an `Intersect`, the last runtype returns a value and preceding intersectees are executed but results are just discarded:\n\n```typescript\nconst FlipFlip = Intersect(\n\tBoolean.withParser(b =\u003e !b),\n\tBoolean.withParser(b =\u003e !!b),\n)\nFlipFlip.parse(true) === true\n```\n\nWhere the members are `Object`s, this behavior doesn't match with TypeScript; this is to avoid [unsound type inference](https://github.com/runtypes/runtypes/pull/443).\n\n```typescript\nconst A = Object({ n: String.withParser(() =\u003e 1 as const) })\nconst B = Object({ n: String.withParser(parseInt) })\nconst AB = A.and(B)\ntype A = Parsed\u003ctypeof A\u003e\ntype B = Parsed\u003ctypeof B\u003e\ntype AB0 = A \u0026 B // { n: 1 } \u0026 { n: number }\ntype AB1 = Parsed\u003ctypeof AB\u003e // { n: number }\n```\n\n## Readonly objects and arrays\n\n`Array` and `Object` runtypes have a special function `.asReadonly()`, that returns the same runtype but the static counterpart is readonly.\n\nFor example:\n\n```typescript\nconst Asteroid = Object({\n\ttype: Literal(\"asteroid\"),\n\tlocation: Vector,\n\tmass: Number,\n}).asReadonly()\ntype Asteroid = Static\u003ctypeof Asteroid\u003e\n// { readonly type: 'asteroid', readonly location: Vector, readonly mass: number }\n\nconst AsteroidArray = Array(Asteroid).asReadonly()\ntype AsteroidArray = Static\u003ctypeof AsteroidArray\u003e\n// readonly Asteroid[]\n```\n\n## Helper functions for `Object`\n\n`Object` runtype has the methods `.pick()` and `.omit()`, which will return a new `Object` with or without specified fields (see [Example](#example) section for detailed definition of `Rank` and `Planet`):\n\n```ts\nconst CrewMember = Object({\n\tname: String,\n\tage: Number,\n\trank: Rank,\n\thome: Planet,\n})\n\nconst Visitor = CrewMember.pick(\"name\", \"home\")\ntype Visitor = Static\u003ctypeof Visitor\u003e // { name: string; home: Planet; }\n\nconst Background = CrewMember.omit(\"name\")\ntype Background = Static\u003ctypeof Background\u003e // { age: number; rank: Rank; home: Planet; }\n```\n\nAlso you can use `.extend()` to get a new `Object` with extended fields:\n\n```ts\nconst PetMember = CrewMember.extend({\n\tspecies: String,\n})\ntype PetMember = Static\u003ctypeof PetMember\u003e\n// { name: string; age: number; rank: Rank; home: Planet; species: string; }\n```\n\nIt is capable of reporting compile-time errors if any field is not assignable to the base runtype. You can suppress this error by using `@ts-ignore` directive or `.omit()` before, and then you'll get an incompatible version from the base `Object`.\n\n```ts\nconst WrongMember = CrewMember.extend({\n\trank: Literal(\"wrong\"),\n\t// Type '\"wrong\"' is not assignable to type '\"captain\" | \"first mate\" | \"officer\" | \"ensign\"'.\n})\n```\n\n## Pattern matching\n\nThe `Union` runtype offers the ability to do type-safe, exhaustive case analysis across its variants using the `match` method:\n\n```ts\nconst isHabitable = SpaceObject.match(\n\tasteroid =\u003e false,\n\tplanet =\u003e planet.habitable,\n\tship =\u003e true,\n)\n\nif (isHabitable(spaceObject)) {\n\t// ...\n}\n```\n\nThere's also a top-level `match` function which allows testing an ad-hoc sequence of runtypes. You should use it along with `when` helper function to enable type inference of the parameters of the case functions:\n\n```ts\nconst makeANumber = match(\n\twhen(Number, n =\u003e n * 3),\n\twhen(Boolean, b =\u003e (b ? 1 : 0)),\n\twhen(String, s =\u003e s.length),\n)\n\nmakeANumber(9) // = 27\n```\n\nTo allow the function to be applied to anything and then handle match failures, simply use an `Unknown` case at the end:\n\n```ts\nconst makeANumber = match(\n\twhen(Number, n =\u003e n * 3),\n\twhen(Boolean, b =\u003e (b ? 1 : 0)),\n\twhen(String, s =\u003e s.length),\n\twhen(Unknown, () =\u003e 42),\n)\n```\n\n## Adding additional properties\n\nYou may want to provide additional properties along with your runtype, such as the default value and utility functions. This can be easily achieved by the `with` method.\n\n```typescript\nconst Seconds = Number.withBrand(\"Seconds\").with({\n\ttoMilliseconds: (seconds: Seconds) =\u003e (seconds * 1000) as Milliseconds,\n})\ntype Seconds = Static\u003ctypeof Seconds\u003e\n\nconst Milliseconds = Number.withBrand(\"Milliseconds\").with({\n\ttoSeconds: (milliseconds: Milliseconds) =\u003e (milliseconds / 1000) as Seconds,\n})\ntype Milliseconds = Static\u003ctypeof Milliseconds\u003e\n```\n\nSometimes defining additional properties requires access to the original runtype itself statically or dynamically:\n\n```typescript\n// Bummer, this won't work because of the circular reference.\nconst pH = Number.withBrand(\"pH\").with({ default: 7 as pH })\ntype pH = Static\u003ctypeof pH\u003e\n```\n\nIn such cases, you have to receive the original runtype by passing a function instead:\n\n```typescript\nconst pH = Number.withBrand(\"pH\").with(self =\u003e ({\n\tdefault: 7 as Static\u003ctypeof self\u003e,\n}))\ntype pH = Static\u003ctypeof pH\u003e\n```\n\n## Function contracts\n\nRuntypes along with constraint checking are a natural fit for enforcing function contracts. You can construct a contract from runtypes for the parameters and return type of the function:\n\n```ts\nconst divide = Contract({\n\t// This must be a runtype for arrays, just like annotating a rest parameter in TS.\n\treceives: Tuple(\n\t\tNumber,\n\t\tNumber.withConstraint(n =\u003e n !== 0 || \"division by zero\"),\n\t),\n\treturns: Number,\n}).enforce((n, m) =\u003e n / m)\n\ndivide(10, 2) // 5\n\ndivide(10, 0) // Throws error: division by zero\n```\n\nContracts can work with `Parser` runtypes:\n\n```typescript\nconst ParseInt = String.withParser(parseInt)\nconst contractedFunction = Contract({\n\treceives: Array(ParseInt),\n\treturns: Array(ParseInt),\n}).enforce((...args) =\u003e args.map(globalThis.String))\ncontractedFunction(\"42\", \"24\") // [42, 24]\n```\n\n## Miscellaneous tips\n\n### Annotating runtypes\n\nThere might be cases that you have to annotate the type of a runtype itself, not of the checked or parsed value. Basically you should use `Runtype.Core` for it to reduce the type inference cost. `Runtype.Core` is a slim version of `Runtype`; it omits the utility methods that are only used to define a runtype from it.\n\nOn the boundaries of a function, the “accept broader, return narrower” principle applies to runtypes of course; it's **_necessary to use `Runtype.Core` in the parameter types_**, and it's better to use `Runtype` in the return type.\n\nWhen you're to introspect the contents of a variable `x` typed as `Runtype.Core`, you'd want to narrow it to `Runtype.Interfaces` first by `Runtype.isRuntype(x)`.\n\n## Related libraries\n\n- [generate-runtypes](https://github.com/simenandre/generate-runtypes#readme) Generates runtypes from structured data. Useful for code generators\n- [json-to-runtypes](https://github.com/runeh/json-to-runtypes#readme) Generates runtypes by parsing example JSON data\n- [rest.ts](https://github.com/hmil/rest.ts) Allows building type safe and runtime-checked APIs\n- [runtypes-generate](https://github.com/typeetfunc/runtypes-generate) Generates random data by `Runtype` for property-based testing\n- [runtyping](https://github.com/johngeorgewright/runtyping) Generate runtypes from static types \u0026 JSON schema\n- [schemart](https://github.com/codemariner/schemart) Generate runtypes from your database schema.","funding_links":[],"categories":["TypeScript","typescript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruntypes%2Fruntypes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fruntypes%2Fruntypes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fruntypes%2Fruntypes/lists"}