{"id":20794805,"url":"https://github.com/code-star/nope","last_synced_at":"2026-04-18T23:39:53.609Z","repository":{"id":35088280,"uuid":"200186112","full_name":"code-star/nope","owner":"code-star","description":"Applicative validation","archived":false,"fork":false,"pushed_at":"2023-01-04T06:05:09.000Z","size":307,"stargazers_count":1,"open_issues_count":11,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-02-19T22:47:45.141Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/code-star.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}},"created_at":"2019-08-02T07:19:26.000Z","updated_at":"2020-04-22T13:58:34.000Z","dependencies_parsed_at":"2023-01-15T13:35:37.631Z","dependency_job_id":null,"html_url":"https://github.com/code-star/nope","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code-star%2Fnope","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code-star%2Fnope/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code-star%2Fnope/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/code-star%2Fnope/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/code-star","download_url":"https://codeload.github.com/code-star/nope/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243138915,"owners_count":20242472,"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":[],"created_at":"2024-11-17T16:17:46.819Z","updated_at":"2025-12-24T23:43:16.895Z","avatar_url":"https://github.com/code-star.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Nope\n\n## Goals\n\n* To be a type-safe alternative to [Yup](https://github.com/jquense/yup)\n* Composable validation (based on concepts from functional programming)\n* Completely type-safe\n* Custom error types\n\n## Introduction\n\n### Simple validation\n\nLet's say we want to verify that a number is positive. We can define the following _validation rule_:\n\n```typescript\nconst isPositive = ValidationRule.test((n: number) =\u003e {\n  return n \u003e= 0 \n    ? Validated.ok() \n    : Validated.error(`${n} is negative`)\n})\n```\n\nThis results in a `ValidationRule\u003cnumber, string\u003e`. The input is a `number` (the first type parameter), and validation might result in a error of the type `string` (the second type parameter).\n\nHow do we use it?\n\n```typescript\nconst validated = isPositive.apply(-4)\n\nif (validated.isValid()) {\n  console.log(`${validated.value} is positive!`)\n} else {\n  console.error(`Error: ${validated.error}`)\n}\n```\n\n### Transformations\n\nLet's suppose we want to make sure that a `string` represents a valid number:\n\n```typescript\nconst isFloat = ValidationRule.test((s: string) =\u003e {\n  const n = Number.parseFloat(s)\n  return isNaN(n) \n    ? Validated.error(`${s} is not a number`) \n    : Validated.ok()\n})\n```\n\nThat's cool, but there's something inefficient about this. We do all this work to parse the `string` as a valid `number`, only to throw that `number` (the fruit of our labour) away once we've verified that it _is_ a `number`. It is likely that we might want to use that number at some later point, and it feels inefficient to parse it twice (once to verify that it is a `number`, and afterwards again to actually be able to use it). To this end, we can create a validation rule with a return a value with `ValidationRule.create`:\n\n```typescript\nconst isFloat = ValidationRule.create((s: string) =\u003e {\n  const n = Number.parseFloat(s)\n  return isNaN(n) \n    ? Validated.error(`${s} is not a number`) \n    : Validated.ok(n)\n})\n```\n\nThe type of `isFloat` is `ValidationRule\u003cstring, string, number\u003e`. The input is a `string` (the first type parameter) which we try to parse, and validation might result in a error of the type `string` (the second type parameter). The output of this validation rule is `number` (the third type parameter).\n\nHow do we use it?\n\n```typescript\nconst validated = isFloat.apply('123.456789')\n\nif (validated.isValid()) {\n  console.log(`${validated.value.toFixed(2)} is a number!`)\n} else {\n  console.error(`Error: ${validated.error}`)\n}\n```\n\nNote the call to `toFixed`, which we are only able to do because `value` is a `number`.\n\nThe return value of a validation rule is where Nope differs from [Yup](https://github.com/jquense/yup). In Yup, the validation (is something valid or not) and the transformation to a valid value are two separate steps. In Nope, this is a single step. This makes it easy to create validation rules that build upon each other, as we'll see next.\n\n### Chaining\n\nWe can combine the two rules into one:\n\n```typescript\nconst isPositiveFloat = isFloat.composeWith(isPositive)\n```\n\nThis validation rule takes a `string` and tries to parse it as a `number`. If it succeeds, it will verify that the number is positive. We can get one of two errors:\n\n* An error stating that the `string` does not contain a number. For example: `'Dog is not a number'`\n* An error stating that the `number` is negative. For example: `'-123.4 is negative`\n\nThere are many ways to combine simple validation rules into more complex validation rules. Take a look at the documentation for [`combine`](#validationrulecombine), [`test`]($validationruletest) or [`many`](#validationrulemany) for example.\n\n### Meta data\n\nIt's not uncommon to need some meta data to properly validate your data. Take the example where we want to verify that a given `Date` is in the past. We need to know the current time to be able to do this:\n\n```typescript\nconst isInPast = ValidationRule.test((date: Date, now: Date) =\u003e {\n  return date \u003c= now\n    ? Validated.ok()\n    : Validated.error(`${date} is after current time (${now})`)\n})\n```\n\nThis results in a `ValidationRule\u003cDate, string, Date, [Date]\u003e`. The input is a `Date` (the first type parameter), and validation might result in a error of the type `string` (the second type parameter). Because we are using `ValidationRule.test` (and not `ValidationRule.create`), validation will result in a `Date` (the third type parameter) which is the original `Date` we pass in. Lastly, the meta data is specified by the parameter list `[Date]` (the fourth type parameter). We can pass as many values in as we'd like.\n\nHow do we use it?\n\n```typescript\nconst date = new Date(2019, 4, 3, 14, 12)\nconst now = new Date(2019, 4, 3, 23, 59)\nconst validated = isInPast.apply(date, now)\n\nif (validated.isValid()) {\n  console.log(`${date} is in the past!`)\n} else {\n  console.error(`Error: ${validated.error}`)\n}\n```\n\n#### Error types\n\nOne of the goals of this library is to properly track all the possible errors, so you can be sure you handle all of them (and not too many). When we want to verify that a value (of type `unknown`) is a `string` containing a positive number we can define the following validation rule:\n\n```typescript\nconst containsPositiveNumber = Strings\n  .fromUnknown()\n  .composeWith(Strings.containsFloat())\n  .composeWith(Numbers.positive())\n```\n\nthe type of this validation rule is `ValidationRule\u003cunknown, NotAString | DoesNotContainFloat | NotPositive, number\u003e`. Contrast this with an error type like [Yup's](https://github.com/jquense/yup), where every error is encoded as a `string`.\n\n## API\n\nNote that this documentation is not yet complete. Explore the API to learn more about what is possible. Help making the documentation complete is very welcome.\n\n- [API](#API)\n  - `ValidationRule`\n    - [`ValidationRule.combine`](#validationrulecombine)\n    - [`ValidationRule.composeWith`](#validationrulecomposewith)\n    - [`ValidationRule.many`](#validationrulemany)\n    - [`ValidationRule.of`](#validationruleof)\n    - [`ValidationRule.test`](#validationruletest)\n    - [`ValidationRule.optional`](#validationruleoptional)\n    - [`ValidationRule.required`](#validationrulerequired)\n    - [`ValidationRule.map`](#validationrulemap)\n    - [`ValidationRule.orElse`](#validationruleorelse)\n    - [`ValidationRule.mapError`](#validationrulemaperror)\n  - `Booleans`\n    - [`Booleans.fromBoolean`](#booleansfromboolean)\n    - [`Booleans.fromUnknown`](#booleansfromunknown) \n  - `Numbers`\n    - [`Numbers.fromNumber`](#numbersfromnumber)\n    - [`Numbers.fromUnknown`](#numbersfromunknown)\n    - [`Numbers.positive`](#numberspositive)\n  - `Strings`\n    - [`Strings.fromString`](#stringsfromstring)\n    - [`Strings.fromUnknown`](#stringsfromunknown)\n    - [`Strings.notEmpty`](#stringsnotempty)\n    - [`Strings.containsFloat`](#stringscontainsfloat)\n  - `Arrays`\n    - [`Arrays.fromArray`](#arraysfromarray)\n\n#### `ValidationRule.combine`\n\nCreates a `ValidationRule` for an object. Define the keys of the object and the validation rules for those keys. \n\n```typescript\nconst isPerson = ValidationRule.combine({\n  age: Numbers.fromNumber().composeWith(Numbers.positive()),\n  name: Strings.fromString().composeWith(Strings.notEmpty())\n})\n```\n\nUsing this validation rule results in a valid object of shape\n\n```typescript\n{\n  age: number,\n  name: string\n}\n```\n\nor an error of shape\n\n```typescript\n{\n  age?: NotPositive,\n  name?: EmptyString\n}\n```\n\n#### `ValidationRule.composeWith`\n\nCompose two validation rules. Produces either of the two errors of the individual validation rules.\n\nFor example,\n\n```typescript\nNumbers.fromUnknown().composeWith(Numbers.positive())\n```\n\nwill produce either a `NotANumber` error or a `NotPositive` error when it fails.\n\nYou can chain as many `composeWith`-calls as you like. The following will check whether an `unknown` value is a string which contains a positive float:\n\n```typescript\nconst containsPositiveFloat = Strings.fromUnknown()\n  .composeWith(Strings.notEmpty())\n  .composeWith(Strings.containsFloat())\n  .composeWith(Numbers.positive())\n```\n\nThis validation rule can result in either:\n\n* A `NotAString` error\n* An `EmptyString` error\n* A `DoesNotContainFloat` error\n* A `NotPositive` error\n\n#### `ValidationRule.many`\n\nFrom a `ValidationRule` for a type `A`, creates a `ValidationRule` for type `A[]`.\n\nContrast with `ValidationRule.of`.\n\n```typescript\nconst areAllPositive = Numbers.positive().many()\n```\n\nUsing this validation rule results in a valid `Array\u003cnumber\u003e` when all input values are positive, or an error of shape `Array\u003cNotPositive | undefined\u003e`. The error `Array` will only have values at the indices where the negative numbers are located.\n\n#### `ValidationRule.of`\n\nGiven that the output type of validation rule is `A[]`, allows you to apply a validation rule that takes an `A` as input.\n\nContrast with `ValidationRule.many`.\n\n```typescript\nconst areAllPositive = Arrays.fromArray\u003cnumber\u003e().of(Numbers.positive())\n```\n\n#### ValidationRule.test\n\nCombining validation rules like `composeWith` produces a union of errors. We can expect _either_ an error of this shape, _or_ an error of that shape. We can never get both errors at the same time.\n\n`ValidationRule.test` allows you to run multiple validation rules at the same time (producing possibly many errors). Those validation rules are used purely for their errors. The values returned from the individual validation rules are discarded.\n\n#### `ValidationRule.optional`\n\nAllows optional values (but doesn't raise an error). Mostly used to wrap a `composeWith`-chain to make the _whole_ chain optional.\n\nContrast this with `ValidationRule.required`.\n\n```typescript\nconst isNumber = Numbers\n  .fromNumber()\n  .composeWith(Numbers.positive())\n  .optional()\n\n// Results in `Validated.ok(undefined)`.\nisNumber.apply(undefined) // OK!\n```\n\n#### `ValidationRule.required`\n\nAllows optional inputs, but raises an error if the input is `undefined`.\n\nContrast this with `ValidationRule.optional`.\n\n```typescript\nconst isNumber = Numbers\n  .fromNumber()\n  .composeWith(Numbers.positive())\n  .required()\n\n// Results in `IsUndefined` error but is allowed by\n// the type system.\nisNumber.apply(undefined)\n```\n\n#### `ValidationRule.map`\n\nApply a transformation function to the result of the validation rule.\n\n#### `ValidationRule.orElse`\n\nReturn the value if it is valid, \"or else\" fall back to the given default.\n\n#### `ValidationRule.mapError`\n\nTransforms the error value, like [`ValidationRule.map`](#validationrulemap) transforms the valid value.\n\n#### `Booleans.fromBoolean`\n\n`ValidationRule` that takes a `boolean` and produces a `boolean`. This rule will not produce any errors. It is usually used as a starting point for more complex validation rules.\n\n#### `Booleans.fromUnknown`\n\n`ValidationRule` that ensures that a given value (of type `unknown`) is a `boolean`. May produce a `NotABoolean` error.\n\n#### `Numbers.fromNumber`\n\n`ValidationRule` that takes a `number` and produces a `number`. This rule will not produce any errors. It is usually used as a starting point for more complex validation rules.\n\n#### `Numbers.fromUnknown`\n\n`ValidationRule` that ensures that a given value (of type `unknown`) is a `number`. May produce a `NotANumber` error.\n\n#### `Numbers.positive`\n\n`ValidationRule` that ensures that a given `number` is positive. May produce a `NotPositive` error.\n\n#### `Strings.fromString`\n\n`ValidationRule` that takes a `string` and produces a `string`. This rule will not produce any errors. It is usually used as a starting point for more complex validation rules.\n\n#### `Strings.fromUnknown`\n\n`ValidationRule` that ensures that a given value (of type `unknown`) is a `string`. May produce a `NotAString` error.\n\n#### `Strings.notEmpty`\n\nVerifies that a given `string` is not empty.\n\n#### `Strings.containsFloat`\n\nVerifies that a given string contains a `number`. Uses `parseFloat` under the hood. Produces a (possible) `number`.\n\n#### `Arrays.fromArray`\n\n`ValidationRule` that takes an array and produces an array. Takes a type parameter to limit the type of acceptable elements. This rule will not produce any errors. It is usually used as a starting point for more complex validation rules.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcode-star%2Fnope","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcode-star%2Fnope","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcode-star%2Fnope/lists"}