Many libraries rely on some sort of type validation. Their maintainers have the choice of either to:
- 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
) - Couple their code with a specific validation library: which limits adoption by developers who use another
- 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 validation libraries
- 🍃 Tiny client footprint, tree-shakeable
- ✨ Easy-to-use, minimal API
Setup
Install TypeSchema with your package manager of choice:
npm | npm install @decs/typeschema |
---|---|
Yarn | yarn add @decs/typeschema |
pnpm | pnpm add @decs/typeschema |
Vite
If using Vite, you'll also need to update your vite.config.ts
file:
import { typeschemaPlugin } from "@decs/typeschema/vite";
export default defineConfig({
plugins: [
typeschemaPlugin(), // add this plugin
],
});
Usage
import type {Infer, InferIn, Schema} from '@decs/typeschema';
import {assert, createAssert, validate} 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 validated data or throws an `AggregateError`
await assert(schema, '123'); // '123'
await assert(schema, 123); // throws `AggregateError`
// Returns the validated data or a list of `ValidationIssue`s
await validate(schema, '123'); // {data: '123'}
await validate(schema, 123); // {issues: [`ValidationIssue`]}
// Returns an assertion function for a specific schema
const assertString = createAssert(schema);
await assertString('123'); // '123'
await assertString(123); // throws `AggregateError`
Coverage
TypeSchema supports all major schema validation libraries:
Project | Popularity | Example schema | Validation | Infer |
InferIn |
---|---|---|---|---|---|
zod | z.string() |
✅ | ✅ | ✅ | |
yup | string() |
✅ | ✅ | ✅ | |
joi | Joi.string() |
✅ | ❌ | ❌ | |
ajv | {type: "string"} |
✅ | ❌ | ❌ | |
superstruct | string() |
✅ | ✅ | ❌ | |
io-ts | t.string |
✅ | ✅ | ✅ | |
ow[^1] | ow.string |
✅ | ✅ | ✅ | |
typia | typia.createAssert<string>() |
✅ | ✅ | ✅ | |
typebox | Type.String() |
✅ | ✅ | ✅ | |
deepkit | typeOf<string>() |
✅ | ❌ | ❌ | |
valibot | string() |
✅ | ✅ | ✅ | |
runtypes | String |
✅ | ✅ | ✅ | |
arktype | type('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 assert(assertString, '123'); // '123'
await assert(assertString, 123); // throws `AggregateError`
await validate(assertString, '123'); // {data: '123'}
await validate(assertString, 123); // {issues: [`ValidationIssue`]}
API
Types
Schema
Generic interface for schemas
An union of the schema types of all supported librariesValidationIssue
Generic interface for validation issues
Includes amessage: string
and an optionalpath?: Array<string | number | symbol>
Infer<TSchema extends Schema>
Extracts the output type of a schema
InferIn<TSchema extends Schema>
Extracts the input type of a schema
Functions
assert(schema, data)
assert<TSchema extends Schema>( schema: TSchema, data: unknown, ): Promise<Infer<TSchema>>
Returns the validated data or throws an
AggregateError
validate(schema, data)
validate<TSchema extends Schema>( schema: TSchema, data: unknown, ): Promise<{data: Infer<TSchema>} | {issues: Array<ValidationIssue>}>
Returns the validated data or a list of
ValidationIssue
screateAssert(schema)
createAssert<TSchema extends Schema>( schema: TSchema, ): (data: unknown) => Promise<Infer<TSchema>>
Returns an assertion function for a specific schema
Acknowledgements
- Inspired by tRPC's input & output validators
- Adapter architecture inspired by @ecyrbe's suggestions
- API definition inspired by @colinhacks's proposal