{"id":13485040,"url":"https://github.com/neuledge/computed-types","last_synced_at":"2025-05-16T13:05:55.497Z","repository":{"id":37021626,"uuid":"245673761","full_name":"neuledge/computed-types","owner":"neuledge","description":"🦩 Joi like validations for TypeScript","archived":false,"fork":false,"pushed_at":"2024-06-16T10:51:30.000Z","size":2345,"stargazers_count":362,"open_issues_count":6,"forks_count":13,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-12T08:38:31.437Z","etag":null,"topics":["async","javascript","runtime","schema","strongly-typed","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/neuledge.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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},"funding":{"github":"neuledge"}},"created_at":"2020-03-07T17:13:23.000Z","updated_at":"2025-02-11T21:57:15.000Z","dependencies_parsed_at":"2024-06-21T16:50:23.355Z","dependency_job_id":null,"html_url":"https://github.com/neuledge/computed-types","commit_stats":{"total_commits":390,"total_committers":9,"mean_commits":"43.333333333333336","dds":"0.46923076923076923","last_synced_commit":"1f93d95d7df547025b6a4490b9435b737c92db92"},"previous_names":["neuledge/funval"],"tags_count":46,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neuledge%2Fcomputed-types","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neuledge%2Fcomputed-types/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neuledge%2Fcomputed-types/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neuledge%2Fcomputed-types/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neuledge","download_url":"https://codeload.github.com/neuledge/computed-types/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253708840,"owners_count":21951060,"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":["async","javascript","runtime","schema","strongly-typed","typescript","validation"],"created_at":"2024-07-31T17:01:43.597Z","updated_at":"2025-05-16T13:05:55.445Z","avatar_url":"https://github.com/neuledge.png","language":"TypeScript","readme":"\u003ch1 align=\"center\" style=\"text-align:center\"\u003e🦩 Computed Types\u003c/h1\u003e\n\n\u003ch4 align=\"center\"\u003eRuntime validation types for TypeScript.\u003c/h4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.org/package/computed-types\"\u003e\n    \u003cimg src=\"http://img.shields.io/npm/v/computed-types.svg\" alt=\"View On NPM\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/neuledge/computed-types/actions/workflows/build.yml\"\u003e\n    \u003cimg src=\"https://github.com/neuledge/computed-types/actions/workflows/build.yml/badge.svg\"\n      alt=\"Build Status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://depfu.com/github/neuledge/computed-types?project_id=12857\"\u003e\n    \u003cimg src=\"https://badges.depfu.com/badges/f49a9b5caba7c3538e1fd9a06a72bca6/overview.svg\"\n      alt=\"Dependency Status\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://codecov.io/gh/neuledge/computed-types\"\u003e\n    \u003cimg src=\"https://codecov.io/gh/neuledge/computed-types/branch/master/graph/badge.svg?token=4YPG4FPM23\"\n      alt=\"Coverage Status\" /\u003e\n  \u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/l/computed-types.svg\" alt=\"License\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\u003cbr\u003e\n\n**Computed-Types** (formerly: [Funval](https://www.npmjs.org/package/funval)) is a strongly-typed\nvalidation library for TypeScript. Using function interfaces, _computed-types_ knows how to\ntransform and validate your data, and automatically generates accurate TypeScript\ninterfaces on compile time.\n\n#### Using `computed-types`:\n\n```ts\nconst UserSchema = Schema({\n  name: string,\n  amount: number,\n  flags: array.of(string).optional();\n});\n\ntype User = Type\u003ctypeof UserSchema\u003e;\n```\n\n#### Equivalent code in `Joi`:\n\n```ts\nconst UserSchema = Joi.object({\n  name: Joi.string().required(),\n  amount: Joi.number().required(),\n  flags: Joi.array().items(Joi.string()),\n});\n\ntype User = {\n  name: string;\n  amount: number;\n  flags?: string[];\n};\n```\n\n### Main Features\n\n- **Easy to Read** - Uses runtime types like in TypeScript (including `string`, `array`, `unknown`,\n  etc...)\n- **Reduce Duplication** - Create new validator using existing functions in seconds.\n- **TypeScript Validation** - Detect errors during compile time as well.\n- **Function Composition** - Chain multiple validators to generate new types.\n- **Data Transformation** - Combine validation and formatting in the one action.\n- **Asynchronous \u0026 Synchronous Support** - Automatically detected promises and async validation.\n- **Zero Dependencies** - Light and compact library.\n- **Pure Javascript** - Also works without TypeScript.\n\n### Sponsored by ❤️\n\nIf you like this project, please [consider sponsoring us](https://github.com/sponsors/neuledge) to help us continue to maintain and improve\nthis project.\n\n\u003cbr\u003e\n\n## Table of Contents\n\n- [Install](#install)\n- [Usage](#usage)\n- [Creating new Types](#creating-new-types)\n- [Validators Chain](#validators-chain)\n- [Documentation](#available-types)\n- [License](#license)\n\n\u003cbr\u003e\n\n## Install\n\n#### Node.js:\n\n```bash\nnpm i computed-types\n```\n\n#### Deno:\n\n```ts\nimport Schema, {\n  Type,\n  string,\n  number,\n  array,\n} from 'https://denoporter.sirjosh.workers.dev/v1/deno.land/x/computed_types/src/index.ts';\n```\n\n\u003cbr\u003e\n\n## Usage\n\n```ts\nimport Schema, { Type, string, number, array } from 'computed-types';\n\nconst UserSchema = Schema({\n  name: string.trim().normalize().between(3, 40).optional(),\n  username: /^[a-z0-9]{3,10}$/,\n  status: Schema.either('active' as const, 'suspended' as const),\n  items: array\n    .of({\n      id: string,\n      amount: number.gte(1).integer(),\n    })\n    .min(1),\n});\n\ntype User = Type\u003ctypeof UserSchema\u003e;\nconst validator = UserSchema.destruct();\n\nconst [err, user] = validator({\n  username: 'john1',\n  // 🚨 TypeScript Error: Type '\"unregistered\"' is not assignable to type '\"active\" | \"suspended\"'.\n  status: 'unregistered',\n  items: [{ id: 'item-1', amount: 20 }],\n});\n\nconsole.log(err);\n// 🚨 ValidationError: Expect value to equal \"suspended\" {\n//   errors: [\n//     {\n//       error: TypeError: Expect value to equal \"suspended\",\n//       path: ['status']\n//     }\n//   ]\n// }\n```\n\n\u003cbr\u003e\n\n## Creating new Types\n\nA computed type is any function that can return a value without throwing any exceptions. Creating a custom type\nallows you to normalize, transform and validate any input.\n\nFor example this type will validate email addresses:\n\n```ts\nimport * as EmailValidator from 'email-validator';\n\nfunction Email(input: unknown): string {\n  if (!EmailValidator.validate(String(input))) {\n    throw new TypeError(`Invalid email address: \"${input}\"`);\n  }\n\n  return input;\n}\n```\n\nYou can use the above validator on schemas as an `Email` type and it will validate inputs in the\nform of `{ email: unknown }` to `{ email: string }` type.\n\n```ts\nconst UserSchema = {\n  email: Email,\n};\n\nconst validator = Schema(UserSchema);\n```\n\nTo create optional types, change the validator arguments to optional as well:\n\n```ts\nfunction OptionalEmail(input?: unknown): string | undefined {\n  return input == null ? undefined : Email(input);\n}\n```\n\nThis will validate inputs in the form of `{ email?: unknown }` to `{ email: string | undefined }`.\n\n\u003cbr\u003e\n\n### Using Transform\n\nThe custom Email validator above will not support [validator chaining](#validators-chain), but we can easily\nfix this by using the [`.transform()` method](#transform).\n\n```ts\nconst EmailWithValidatorChain = unknown.string.transform(Email);\n```\n\nI can now make use of the validator chain:\n\n```ts\nconst UserSchema = {\n  email: EmailWithValidatorChain.optional().max(100),\n};\n\nconst validator = Schema(UserSchema);\n```\n\n\u003cbr\u003e\n\n### Asynchronous Validators\n\nAsynchronous validators are supported by returning a `Promise` (or `PromiseLike`) values:\n\n```ts\nimport fetch from 'node-fetch';\n\nasync function AvailableUsername(input: string): Promise\u003cstring\u003e {\n  const res = await fetch(\n    `/check-username?username=${encodeURIComponent(input)}`,\n  );\n\n  if (!res.ok) {\n    throw new TypeError(`Username \"${input}\" is already taken`);\n  }\n\n  return input;\n}\n```\n\n_Computed-types_ automatically detects promise and convert the return type of the `Validator` to\npromise as well:\n\n```ts\nconst UserSchema = {\n  username: AvailableUsername,\n};\nconst validator = Schema(UserSchema);\n\nconst user = await validator({ username: 'test' });\n```\n\nTrying to access the return value without resolving it with promise first will detect and alert\nautomatically via TypeScript on compile time.\n\n\u003cbr\u003e\n\n## Validators Chain\n\nEvery validator in `\"computed-types\"` is a validation function that can be called and validate\nany sort of data. In addition, each validator has a few helper methods to chain multiple\nvalidators together.\n\nFor example, check out this use case:\n\n```ts\nimport { unknown } from 'computed-types';\n\nconst validator = unknown.number().gt(0).toFixed(2);\n\nconsole.log(validator('123.4567')); // '123.46'\n```\n\nYou can [see here](#available-types) all the custom chain methods for each type. Please note that\nafter calling `toFixed`, the validator no longer returns a `number` but a\n`string` so all the helpers functions available after `toFixed` will be the `string` helpers.\n\nIn addition the type helpers, each validator has those default chain helpers so use:\n\n- [`.equals()`](#equals)\n- [`.test()`](#test)\n- [`.transform()`](#transform)\n- [`.construct()`](#construct)\n- [`.optional()`](#optional)\n- [`.strictOptional()`](#strictOptional)\n- [`.destruct()`](#destruct)\n- [`.error()`](#error)\n\n\u003cbr\u003e\n\n##### `.equals()`\n\nVerify the return value equals to the given value.\n\n```ts\nconst validator = boolean.equals(true);\n```\n\n##### `.test()`\n\nVerify the return value pass the given test function.\n\n```ts\nimport * as EmailValidator from 'email-validator';\n\nconst validator = string.test(EmailValidator.validate, 'Invalid email address');\n```\n\n##### `.transform()`\n\nTransform the return value to a new value or throw to fail the validation process. The return\nvalue can be any value, including different types.\n\n```ts\nconst validator = number.transform((x): number =\u003e {\n  if (x \u003c= 0) {\n    throw new RangeError('Expected number to be positive');\n  }\n\n  return Math.sqrt(x);\n});\n```\n\n##### `.construct()`\n\nSimilar to [`.transform()`](#transform) but less common. This helper is useful when you want to\nchange the validator input before validating it. The returning value of the construct function\nshould always return an array as this array will pass to the original validator input as arguments.\n\n```ts\nconst validator = number.gt(1).construct((x: number, y: number) =\u003e [x + y]);\nvalidators(x, y); // x + y\n```\n\n##### `.optional()`\n\nWill convert the validator to an optional by allowing `undefined` or `null` values.\nThis is very useful for parsing when creating optional properties on a schema.\n\n```ts\nconst validator = Schema({\n  name: string.trim().min(1),\n  address: string.trim().optional(),\n});\n```\n\n##### `.strictOptional()`\n\nSame as `.optional()` but allows only `undefined` values.\n\n```ts\nconst validator = Schema({\n  name: string.trim().min(1),\n  address: string.trim().optional(),\n});\n```\n\n##### `.destruct()`\n\nUse this as the final helper on the chain. It will catch any validation error and spread it to a\n2-arguments array with an error and possible value on success. Useful if you don't like catching\nerrors.\n\n```ts\nconst validator = Schema({\n  name: string.trim().min(1),\n}).destruct();\n\nconst [err, user] = validator(req.body);\n```\n\n##### `.error()`\n\nWill catch any error and replace it with your custom error instead. You can pass a `string`,\n`ValidationError` or a `function` that will generate an error for you. Notice that on most cases you\nwill not need to use this helpers, as most validation helpers has an optional `error` param with the\nsame functionality.\n\n```ts\nconst validator = Schema({\n  name: string.error('expect input to be string'),\n  amount: number.gt(0, (val) =\u003e `${val} is not positive amount`);\n});\n```\n\n\u003cbr\u003e\n\n## Available Types\n\nIt's useful to import the following native types when building custom schemas.\nClick on each type to see some validation examples.\n\n**`import`** [`Schema`](#schema), `{` [`unknown`](#unknown), [`string`](#string), [`number`](#number), [`boolean`](#boolean), [`array`](#array), [`DateType`](#datetype) `}` **`from`** `'computed-types';`\n\n\u003cbr\u003e\n\n### `Schema`\n\nCreate a validator from schema object, values or function validators.\n\n```ts\nconst validator = Schema(\n  {\n    name: string,\n    amount: number,\n  },\n  'Missing name or amount',\n);\n```\n\n##### Strict mode\n\nBy default, the schema validator will ignore all properties that aren't exist on the schema. If\nyou want to throw an error instead you can toggle the strict mode on.\n\n```ts\nconst validator = Schema(\n  {\n    name: string,\n    amount: number,\n  },\n  { strict: true },\n);\n```\n\n##### `Schema.either`\n\nWorks as OR switch. Create a validator from multiple function validators or schema objects.\n\n```ts\nconst validator = Schema.either({ foo: string }, { bar: number });\n// validate: { foo: string; } | { bar: number; }\n```\n\n##### `Schema.merge`\n\nWorks as AND switch. Create a validator from multiple function validators or schema objects.\n\n```ts\nconst validator = Schema.merge({ foo: string }, { bar: number });\n// validate: {\n//   foo: string;\n//   bar: number;\n// }\n```\n\n##### `Schema.enum`\n\nCreate a validator from TypeScript enum.\n\n```ts\nenum Status {\n  OK,\n  Invalid,\n}\n\nconst validator = Schema.enum(Status, 'Invalid status');\n```\n\n##### `Schema.record`\n\nCreate a `Record\u003ckey, value\u003e` validator.\n\n```ts\nconst validator = Schema.record(string.regexp(/^[a-z]+$/), number);\n```\n\n\u003cbr\u003e\n\n### `unknown`\n\nAccept any `unknown` value:\n\n```ts\nconst validator = Schema({\n  data: unknown,\n});\n```\n\n##### `unknown.schema()`\n\nAccept any value as an input and try to convert it the given schema:\n\n```ts\nconst validator = unknown.schema({\n  foo: string.trim(),\n});\n```\n\n##### `unknown.object()`\n\nAccept any value as an input and try to convert it to an object:\n\n```ts\nconst validator = unknown.object('Expect data to be an object');\n```\n\n##### `unknown.array()`\n\nAccept any value as an input and try to convert it to an array:\n\n```ts\nconst validator = unknown.array().min(1).of(boolean);\n```\n\n##### `unknown.string()`\n\nAccept any value as an input and try to convert it to a string:\n\n```ts\nconst validator = unknown.string('Expect data to be string').toUpperCase();\n\n// will accept: `{ data: 1 }` and convert it to `{ data: '1' }`\n// will throw: `{ data: null }`\n```\n\n##### `unknown.number()`\n\nAccept any value as an input and try to convert it to a number:\n\n```ts\nconst validator = unknown.number('Expect data to be number').gt(0);\n```\n\n##### `unknown.boolean()`\n\nAccept any value as an input and try to convert it to a boolean:\n\n```ts\nconst validator = unknown.boolean('Expect data to be boolean').equals(true);\n```\n\n##### `unknown.date()`\n\nAccept any value as an input and try to convert it to a date:\n\n```ts\nconst validator = unknown\n  .date('Expect data to be date')\n  .equals('1970-01-01T00:00:00.050Z');\n```\n\n##### `unknown.enum()`\n\nAccept any value as an input and try to convert it to the given enum:\n\n```ts\nenum Status {\n  OK,\n  Invalid,\n}\n\nconst validator = unknown.enum(Status);\n```\n\n##### `unknown.record()`\n\nAccept any value as an input and try to convert it to a `Record\u003ckey, value\u003e`:\n\n```ts\nconst validator = unknown.record(string, number);\n```\n\n\u003cbr\u003e\n\n### `string`\n\nAccept only string values (including empty strings).\n\n```ts\nconst validator = Schema({\n  content: string,\n});\n```\n\n##### `string.toLowerCase()`\n\nAccept string and convert it to lower case.\n\n```ts\nconst validator = string.toLowerCase().trim();\n```\n\n##### `string.toUpperCase()`\n\nAccept string and convert it to upper case.\n\n```ts\nconst validator = string.toUpperCase().trim();\n```\n\n##### `string.toLocaleLowerCase()`\n\nAccept string and convert it to local lower case.\n\n```ts\nconst validator = string.toLocaleLowerCase('en-US').trim();\n```\n\n##### `string.toLocaleUpperCase()`\n\nAccept string and convert it to local upper case.\n\n```ts\nconst validator = string.toLocaleUpperCase('en-US').trim();\n```\n\n##### `string.trim()`\n\nAccept string and trim it.\n\n```ts\nconst validator = string.trim();\n```\n\n##### `string.truncate()`\n\nTruncate a string to a given length with ellipsis (`…`) to the end. If the string below the given\nlimit the original string is return.\n\n```ts\nconst validator = string.truncate(100);\n```\n\n##### `string.normalize()`\n\nAccept string and normalize it.\n\n```ts\nconst validator = string.normalize();\n```\n\n##### `string.min()`\n\nAccept string with minimum given length.\n\n```ts\nconst validator = string.min(2).toLowerCase();\n```\n\n##### `string.max()`\n\nAccept string with maximum given length.\n\n```ts\nconst validator = string.max(10).toUpperCase();\n```\n\n##### `string.between()`\n\nAccept string within the given length range.\n\n```ts\nconst validator = string.between(2, 10).trim();\n```\n\n##### `string.regexp()`\n\nAccept only strings that match the given regular expression.\n\n```ts\nconst validator = string.regexp(/^Hello/).trim();\n```\n\n\u003cbr\u003e\n\n### `number`\n\nAccept only number type values.\n\n```ts\nconst validator = Schema({\n  amount: number,\n});\n```\n\n##### `number.float()`\n\nAccept only floating numbers (throws on NaN or non-finite values).\n\n```ts\nconst validator = number.float().gt(0);\n```\n\n##### `number.integer()`\n\nAccept only integer numbers.\n\n```ts\nconst validator = number.integer().gt(0);\n```\n\n##### `number.toExponential()`\n\nAccept number and convert it to exponential format string.\n\n```ts\nconst validator = number.toExponential().toUpperCase();\n```\n\n##### `number.toFixed()`\n\nAccept number and convert it to fixed format string.\n\n```ts\nconst validator = number.toFixed(3);\n```\n\n##### `number.toLocaleString()`\n\nAccept number and convert it to locale string.\n\n```ts\nconst validator = number.toLocaleString('en-US');\n```\n\n##### `number.toPrecision()`\n\nAccept number and convert it to precision string.\n\n```ts\nconst validator = number.toPrecision(2);\n```\n\n##### `number.toString()`\n\nAccept number and convert it to string.\n\n```ts\nconst validator = number.toString(16).toUpperCase();\n```\n\n##### `number.gte()`\n\nAccept number that greater or equal than the boundary given.\n\n```ts\nconst validator = number.gte(1.5);\n```\n\n##### `number.lte()`\n\nAccept number that lower or equal than the boundary given.\n\n```ts\nconst validator = number.lte(10.5);\n```\n\n##### `number.gt()`\n\nAccept number that greater than the boundary given.\n\n```ts\nconst validator = number.gt(1.5);\n```\n\n##### `number.lt()`\n\nAccept number that lower than the boundary given.\n\n```ts\nconst validator = number.lt(10.5);\n```\n\n##### `number.between()`\n\nAccept number between the given boundaries.\n\n```ts\nconst validator = number.between(0, 1);\n```\n\n\u003cbr\u003e\n\n### `boolean`\n\nAccept only boolean type values.\n\n```ts\nconst validator = Schema({\n  agree: boolean,\n});\n```\n\n\u003cbr\u003e\n\n### `array`\n\nAccept only array type values.\n\n````ts\nconst validator = Schema({\n  agree: array\n});\n\n##### `array.of()`\n\nAccept only array with given items.\n\n```ts\nconst numbers = array.of(number); // numbers[]\nconst tuple = array.of(number).between(1, 2); // [number, number?]\nconst objects = array.of({ foo: number }); // { foo: number }[]\nconst enums = array.of(Schema.enum(Status); // Status[]\n````\n\n##### `array.min()`\n\nAccept only array with minimum given items.\n\n```ts\nconst validator = array.min(2);\n```\n\n##### `array.max()`\n\nAccept only array with maximum given items.\n\n```ts\nconst validator = array.max(10);\n```\n\n##### `array.between()`\n\nAccept only array with minimum and maximum count of items.\n\n```ts\nconst validator = array.between(2, 10);\n```\n\n\u003cbr\u003e\n\n### `DateType`\n\nAccept only instances of `Date`.\n\n```ts\nconst validator = Schema({\n  eventTime: DateType,\n});\n```\n\n##### `DateType.toISOString()`\n\nAccept Date and convert it to ISO date string.\n\n```ts\nconst validator = DateType.toISOString();\n```\n\n##### `DateType.getTime()`\n\nAccept Date and convert it to a timestamp.\n\n```ts\nconst validator = DateType.getTime().gt(100);\n```\n\n##### `DateType.gte()`\n\nAccept Date that greater or equal than the boundary given.\n\n```ts\nconst validator = DateType.gte(new Date('2020-10-01T10:00:00.000Z'));\n```\n\n##### `DateType.lte()`\n\nAccept Date that lower or equal than the boundary given.\n\n```ts\nconst validator = DateType.lte(new Date('2020-10-01T10:00:00.000Z'));\n```\n\n##### `DateType.gt()`\n\nAccept Date that greater than the boundary given.\n\n```ts\nconst validator = DateType.gt(new Date('2020-10-01T10:00:00.000Z'));\n```\n\n##### `DateType.lt()`\n\nAccept Date that lower than the boundary given.\n\n```ts\nconst validator = DateType.lt(new Date('2020-10-01T10:00:00.000Z'));\n```\n\n##### `DateType.between()`\n\nAccept Date between the given boundaries.\n\n```ts\nconst validator = DateType.between(\n  new Date('2020-09-01T10:00:00.000Z'),\n  new Date('2020-10-01T10:00:00.000Z'),\n);\n```\n\n\u003cbr\u003e\n\n## License\n\n[MIT](LICENSE) license \u0026copy; 2020 [Neuledge](https://neuledge.com)\n","funding_links":["https://github.com/sponsors/neuledge"],"categories":["TypeScript","Modules","typescript","基础设施","Validation"],"sub_categories":["Utils","Assistants","JAM Stack/静态站点","Runtime"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneuledge%2Fcomputed-types","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneuledge%2Fcomputed-types","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneuledge%2Fcomputed-types/lists"}