Skip to main content

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, arktype, typia)
  2. Couple their code with a specific validation library: which limits adoption by developers who use another
  3. 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

import type {Infer, InferIn, Schema} from '@decs/typeschema';
import {assert, validate, wrap} from '@decs/typeschema';

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

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

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

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

// Returns the validated data or throws an `AggregateError`
await assert(schema, '123'); // '123'
await assert(schema, 123); // throws `AggregateError`

tRPC

You can use any supported schema on tRPC through the wrap function:

import {wrap} from '@decs/typeschema';
import {initTRPC} from '@trpc/server';
import {object, string} from 'valibot';

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

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

Coverage

TypeSchema supports all major schema validation libraries:

Project Popularity wrap validate
assert
Infer InferIn Example schema
zod GitHub stars βœ… βœ… βœ… βœ… z.string()
yup GitHub stars βœ… βœ… βœ… βœ… string()
joi GitHub stars βœ… βœ… ❌ ❌ Joi.string()
ajv GitHub stars βœ… βœ… ❌ ❌ {type: "string"}
superstruct GitHub stars βœ… βœ… βœ… ❌ string()
io-ts GitHub stars βœ… βœ… βœ… βœ… t.string
valibot GitHub stars βœ… βœ… βœ… βœ… string()
typebox GitHub stars βœ… βœ… βœ… βœ… Type.String()
typia GitHub stars βœ… βœ… βœ… βœ… typia.createAssert<string>()
ow[^1] GitHub stars βœ… βœ… βœ… βœ… ow.string
effect GitHub stars βœ… βœ… βœ… βœ… S.string
arktype GitHub stars βœ… βœ… βœ… βœ… type('string')
deepkit GitHub stars βœ… βœ… ❌ ❌ typeOf<string>()
runtypes GitHub stars βœ… βœ… βœ… βœ… String

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

Custom validations are also supported:

export function assertString(data: unknown): string {
  if (typeof data !== 'string') {
    throw new Error('Expected a string, got: ' + data);
  }
  return data;
}

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

await assert(assertString, '123'); // '123'
await assert(assertString, 123); // throws `AggregateError`

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

API

Types

  • Schema

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

  • TypeSchema<TOutput, TInput = TOutput>

    Interface for a wrapped schema, exposing all its operations

  • Infer<TSchema extends Schema>

    Extracts the output type of a schema

  • InferIn<TSchema extends Schema>

    Extracts the input type of a schema

  • ValidationIssue

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

Functions

  • wrap(schema)

    wrap<TSchema extends Schema>(
      schema: TSchema,
    ): TypeSchema<Infer<TSchema>, InferIn<TSchema>>

    Returns the wrapped schema with access to all its operations

  • validate(schema, data)

    validate<TSchema extends Schema>(
      schema: TSchema,
      data: unknown,
    ): Promise<ValidationResult<Infer<TSchema>>>

    Returns the validated data or a list of ValidationIssues

  • assert(schema, data)

    assert<TSchema extends Schema>(
      schema: TSchema,
      data: unknown,
    ): Promise<Infer<TSchema>>

    Returns the validated data or throws an AggregateError

Acknowledgements