TypeSchema

Universal adapter for TypeScript schema validation.

README


TypeSchema

  TypeSchema

https://typeschema.com

  Universal adapter for schema validation



License Bundle size npm downloads GitHub stars

Setup   •   API   •   Coverage   •   GitHub   •   npm   •   Deno



Many libraries rely on some sort of type validation. Their maintainers have the choice of either to:

1. ⁠Implement their own validation logic: which leads to more code to maintain, and we already have many good solutions out there (e.g. [zod](https://zod.dev), [arktype](https://arktype.io), [typia](https://typia.io))
1. Couple their code with a specific validation library: which limits adoption by developers who use another
1. Support multiple validation libraries: which is a burden to keep up-to-date (e.g. tRPC)

There's no best validation library because there's always a tradeoff. Each developer chooses the library that makes the most sense to them. TypeSchema solves this problem by easily providing option 3: support multiple validation libraries out-of-the-box.

Features


- 🚀 Decouple from schema validation libraries
- 🍃 Tiny client footprint, tree-shakeable
- 🛋️ Easy-to-use, minimal API

Usage


  1. ```ts
  2. import type {Infer, InferIn, Schema} from '@decs/typeschema';
  3. import {assert, validate, wrap} from '@decs/typeschema';

  4. // Use your favorite validation library, e.g. `zod`, `arktype`, `typia`
  5. const schema: Schema = z.string();
  6. const schema: Schema = type('string');
  7. const schema: Schema = typia.createAssert<string>();

  8. // Extracts the schema type
  9. type Output = Infer<typeof schema>; // `string`
  10. type Input = InferIn<typeof schema>; // `string`

  11. // Returns the wrapped schema with access to all its operations
  12. const wrapped = wrap(schema);
  13. await wrapped.validate('123'); // {success: true, data: '123'}
  14. await wrapped.assert('123'); // '123'

  15. // Returns the validated data or a list of `ValidationIssue`s
  16. await validate(schema, '123'); // {success: true, data: '123'}
  17. await validate(schema, 123); // {success: false, issues: [`ValidationIssue`]}

  18. // Returns the validated data or throws an `AggregateError`
  19. await assert(schema, '123'); // '123'
  20. await assert(schema, 123); // throws `AggregateError`
  21. ```

tRPC


You can use any supported schema on tRPC through thewrap function:

  1. ```ts
  2. import {wrap} from '@decs/typeschema';
  3. import {initTRPC} from '@trpc/server';
  4. import {object, string} from 'valibot';

  5. // Use your favorite validation library, e.g. `valibot`
  6. const schema = object({name: string()});

  7. const t = initTRPC.create();
  8. const appRouter = t.router({
  9.   hello: t.procedure
  10.     .input(wrap(schema)) // like this
  11.     .query(({input}) => `Hello, ${input.name}!`),
  12. });
  13. ```

Coverage


TypeSchema supports all major schema validation libraries:

| Project | Popularity | `wrap` | `validate`
`assert` | `Infer` | `InferIn` | Example schema |
|:---------------------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------:|:-----------------------:|:-------:|:---------:|:------------------------------|
| [zod](https://zod.dev) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `z.string()` || [yup](https://github.com/jquense/yup) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `string()` || [joi](https://joi.dev) | GitHub stars | ✅ | ✅ | ❌ | ❌ | `Joi.string()` || [ajv](https://ajv.js.org) | GitHub stars | ✅ | ✅ | ❌ | ❌ | `{type: "string"}` || [superstruct](https://docs.superstructjs.org) | GitHub stars | ✅ | ✅ | ✅ | ❌ | `string()` || [io-ts](https://gcanti.github.io/io-ts) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `t.string` || [ow](https://sindresorhus.com/ow)[^1] | GitHub stars | ✅ | ✅ | ✅ | ✅ | `ow.string` || [typia](https://typia.io) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `typia.createAssert()` || [typebox](https://github.com/sinclairzx81/typebox) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `Type.String()` || [valibot](https://valibot.dev) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `string()` || [deepkit](https://deepkit.io) | GitHub stars | ✅ | ✅ | ❌ | ❌ | `typeOf()` || [runtypes](https://github.com/pelotom/runtypes) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `String` || [effect](https://effect.website) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `S.string` || [arktype](https://arktype.io) | GitHub stars | ✅ | ✅ | ✅ | ✅ | `type('string')` |

[^1]: For ow, only v0.28.2 is supported (sindresorhus/ow#248)

Custom validations are also supported:

  1. ```ts
  2. export function assertString(data: unknown): string {
  3.   if (typeof data !== 'string') {
  4.     throw new Error('Expected a string, got: ' + data);
  5.   }
  6.   return data;
  7. }

  8. await validate(assertString, '123'); // {success: true, data: '123'}
  9. await validate(assertString, 123); // {success: false, issues: [`ValidationIssue`]}

  10. await assert(assertString, '123'); // '123'
  11. await assert(assertString, 123); // throws `AggregateError`
  12. ```

Setup


Install TypeSchema with your package manager of choice:

npm npm install @decs/typeschema
Yarn yarn add @decs/typeschema
pnpm pnpm add @decs/typeschema
Deno https://deno.land/x/typeschema

Vite


If using Vite, you'll also need to update yourvite.config.ts file:

  1. ```ts
  2. import {typeschemaPlugin} from '@decs/typeschema/vite';

  3. export default defineConfig({
  4.   plugins: [
  5.     typeschemaPlugin(), // add this plugin
  6.   ],
  7. });
  8. ```

API


Types


- Schema

Generic interface for schemas
An union of the schema types of all supported libraries

- `TypeSchema`

  Interface for a wrapped schema, exposing all its operations

- `Infer`

  Extracts the output type of a schema

- `InferIn`

  Extracts the input type of a schema

- ValidationIssue

Generic interface for validation issues
Includes a `message` and an optional `path`

Functions


- wrap(schema)

  ts
wrap(
    schema: TSchema,
): TypeSchema, InferIn>
  

  Returns the wrapped schema with access to all its operations

- validate(schema, data)

  ts
validate(
    schema: TSchema,
    data: unknown,
): Promise>>
  

  Returns the validated data or a list of ValidationIssues

- assert(schema, data)

  ts
assert(
    schema: TSchema,
    data: unknown,
): Promise>
  

  Returns the validated data or throws an AggregateError

Acknowledgements


- Adapter architecture inspired by @ecyrbe's suggestions
- API definition inspired by @colinhacks's proposal
- Logo designed by flaticon