Giter Club home page Giter Club logo

typeschema's Introduction


TypeSchema

TypeSchema

Universal adapter for schema validation
โœจ https://typeschema.com โœจ


License Bundle size npm downloads GitHub stars

Quickstart ย ย โ€ขย ย  Coverage ย ย โ€ขย ย  API ย ย โ€ขย ย  GitHub ย ย โ€ขย ย  npm


When fetching data from an external source, it's important to verify its integrity. This happens when processing user inputs, calling third-party APIs, loading configuration files, and so on. And the thing is: Typescript doesn't come with runtime validation. Any type assertions are removed at compile-time.

As a result, developers turn to third-party validation libraries. But that landscape is fragmented, lacking a single best option. Each offers different trade-offs on developer experience, bundle size, and community support.

TypeSchema enables writing code that works with any validation library out-of-the-box. It provides a universal adapter for interacting with any validation schema, decoupling from implementation specifics and increasing compatibility.

import {validate} from '@typeschema/main';

import {z} from 'zod';
import {string} from 'valibot';

const zodSchema = z.string();
await validate(zodSchema, '123');
//    ^? {success: true, data: '123'}

const valibotSchema = string();
await validate(valibotSchema, 123);
//    ^? {success: false, issues: [...]}

Quickstart

We value flexibility, which is why there are multiple ways of using TypeSchema:

  1. Using an adapter directly (e.g. @typeschema/valibot): Best pick for end developers, when the validation library is known ahead of time. This is particularly useful for supporting more validation libraries on tRPC.
  2. Handpicking adapters with @typeschema/main: Recommended for library maintainers. Any validation library can be used, but adapters have to be explicitly installed. This allows end developers to trade-off between coverage and bundle size.
  3. Batteries included with @typeschema/all: Easiest to use. All adapters are automatically installed, including future ones. This is a drop-in replacement for the deprecated @decs/typeschema.

Coverage

Project Popularity Infer InferIn Validation Serialization Adapter Downloads
zod GitHub stars โœ… โœ… โœ… โœ… @typeschema/zod npm downloads
yup GitHub stars โœ… โœ… โœ… โœ… @typeschema/yup npm downloads
joi GitHub stars ๐Ÿง ๐Ÿง โœ… โœ… @typeschema/joi npm downloads
ajv GitHub stars โœ… โœ… โœ… โœ… @typeschema/json npm downloads
class-validator GitHub stars โœ… โœ… โœ… ๐Ÿง @typeschema/class-validator npm downloads
superstruct GitHub stars โœ… ๐Ÿง โœ… ๐Ÿง @typeschema/superstruct npm downloads
io-ts GitHub stars โœ… โœ… โœ… ๐Ÿง @typeschema/io-ts npm downloads
valibot GitHub stars โœ… โœ… โœ… โœ… @typeschema/valibot npm downloads
typebox GitHub stars โœ… โœ… โœ… โœ… @typeschema/typebox npm downloads
effect GitHub stars โœ… โœ… โœ… โœ… @typeschema/effect npm downloads
typia GitHub stars โœ… โœ… โœ… ๐Ÿง @typeschema/function npm downloads
ow GitHub stars โœ… โœ… โœ… ๐Ÿง @typeschema/ow npm downloads
arktype GitHub stars โœ… โœ… โœ… ๐Ÿง @typeschema/arktype npm downloads
deepkit GitHub stars ๐Ÿง ๐Ÿง โœ… ๐Ÿง @typeschema/deepkit npm downloads
runtypes GitHub stars โœ… โœ… โœ… ๐Ÿง @typeschema/runtypes npm downloads
fastest-validator GitHub stars ๐Ÿง ๐Ÿง โœ… ๐Ÿง @typeschema/fastest-validator npm downloads
vine GitHub stars โœ… โœ… โœ… ๐Ÿง @typeschema/vine npm downloads
suretype GitHub stars โœ… โœ… โœ… โœ… @typeschema/suretype npm downloads
valita GitHub stars โœ… โœ… โœ… ๐Ÿง @typeschema/valita npm downloads

Note

Don't see your favorite validation library? We welcome PRs! Otherwise, please file an issue to help us prioritize. ๐Ÿ™Œ

API

Inference

  • Infer<TSchema>: Extracts the output type of a schema
  • InferIn<TSchema>: Extracts the input type of a schema

Validation

  • wrap(schema): Returns the wrapped schema with access to its operations
  • validate(schema, data): Returns the validated data or a list of validation issues
  • assert(schema, data): Returns the validated data or throws an AggregateError

Serialization

  • toJSONSchema(schema): Converts the schema into the equivalent JSON schema

Acknowledgements

typeschema's People

Contributors

decs avatar diogob avatar ecyrbe avatar github-actions[bot] avatar jessekelly881 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

typeschema's Issues

Support synchronous validate

At the moment the the validate function is returning a promise, is there a way to support synchronous validation and rename the current validate function to asyncValidate?

Error: Missing adapters for schema: [object Object]

I am using valibot in a Svelte app. Using "@decs/typeschema": "^0.7.0" and "valibot": "^0.8.0"

Not getting a type error, but I am getting this message as a runtime error.

Error: Missing adapters for schema: [object Object]

Example:

// valibot schema
export const newCharacterSchema = object({
	name: string([minLength(1, "Required")]),
	campaign: string([minLength(1, "Required")]),
	race: useDefault(string(), ""),
	class: useDefault(string(), ""),
	character_sheet_url: useDefault(string([url()]), ""),
	image_url: useDefault(string([url()]), "")
});
<!-- +page.svelte -->
<SchemaForm ... schema={newCharacterSchema} ...>
	...
</SchemaForm>
<!-- SchemaForm.svelte -->
<script lang="ts" generics="TSchema extends Schema">
import type { Infer, InferIn, Schema } from "@decs/typeschema";
import { validate } from "@decs/typeschema";
	
export let schema: TSchema;
export let data: InferIn<TSchema>;
export let validatedData: Infer<TSchema> | undefined = undefined;

export let errors = new SvelteMap<string, string>();
$: checkErrors(data);

function checkErrors(data: InferIn<TSchema>) {
	errors = errors.clear();
	validate(schema, data).then((result) => {
		if ("issues" in result) {
			result.issues.forEach((issue) => {
				if (issue.path) errors = errors.set(issue.path.join("."), issue.message);
			});
		}
		if ("data" in result) {
			validatedData = result.data;
		}
	});
}
</script>

What am I doing wrong?

`validate` function breaks Next.js edge runtime

Hi, a user of next-safe-action reported a bug with the edge runtime, where the action execution breaks with this error when client input(s) get validated and parsed:

TypeError [ERR_VM_DYNAMIC_IMPORT_CALLBACK_MISSING]: A dynamic import callback was not specified.

I created a repo with a minimal reproduction of the issue, which appears to be directly related to TypeSchema validate function.

Now, apparently Vercel is abandoning edge rendering and I don't know if this "bug" is easily fixable, so please let me know if I can do something to help solving this one, if possible.

Here's the original issue opened in next-safe-action repo:

Thanks!

Upvote & Fund

  • We're using Polar.sh so you can upvote and help fund this issue.
  • We receive the funding once the issue is completed & confirmed by you.
  • Thank you in advance for helping prioritize & fund our backlog.
Fund with Polar

"Module not found" errors on next.js

I had to downgrade to 0.11.0 because the library was asking me to install all the peer dependencies.

Tried with pnpm and npm. Same error.

./node_modules/.pnpm/@[email protected][email protected]/node_modules/@decs/typeschema/dist/deepkit-KEFZXADV.mjs:2:0
Module not found: Can't resolve '@deepkit/type'

https://nextjs.org/docs/messages/module-not-found

Import trace for requested module:
./node_modules/.pnpm/@[email protected][email protected]/node_modules/@decs/typeschema/dist/index.mjs
./src/server/routers/auth/login.ts
./src/server/routers/auth.ts
./src/server/index.ts
./src/app/api/trpc/[trpc]/route.ts

It seems there is an error where the _valibot variant is calling for the other deps.

Add JSON Schema export to the API?

Now that its decided that TypeSchema will be used in Superforms v2, I'm seeing the need for a Validation Schema to JSON Schema feature for the validation libraries.

I'm planning to do this with similar kind adapters as in TypeSchema, but then I was thinking "why shouldn't that be a part of TypeSchema, or its own library?" So I wonder if it would be feasible to build this into TypeSchema? Of course, I'll be contributing to that feature.

Document which versions are supported

For example, you cannot use this library with Zod 3.21.4, only 3.22.2. It would be great if the README table of supported validation libraries included the supported versions.

Proposal: common set schema api

Hi, Thank you for this lib.

It would be nice if we can abstract a common set of schema apis (e.g. merge schemas) available for lib authors.

I am happy to help implementing this if you think this is a valid requirement.

Typeschema is no more extensible

New design makes registry no more extensible by users reverting the design to what it was before (ie: adapter are not self contained)
Making users dependant on typeschema needing to add support for niche libraries.
Making it not trully agnostic library.
is this temporary or a design decision ?

TypeSchema breaks Next.js Turbopack dev server and edge deployment

Hi, I'm using TypeSchema in next-safe-action, which is a Next.js type safe wrapper for React Sever Actions.

Some users found out that since version 6 of the library, Next.js dev server using Turbopack (next dev --turbo) and deployments on Vercel using edge runtime are broken, due to TypeSchema internal errors. Additional information about these errors can be found in this issue.

I'm not sure if these problems can be fixed easily, but I'm happy to help in any way I can. Thank you!

Schema is wrongly typed for schemas having output!=input

Hello,

I did some tests on some more advanced schemas.

There are some issues with current approach to infer the type for libraries supporting inputs that are different than outputs.
For example, in arktype, adding transformations, the type T of the schema is not equal the typescript type to output :
image

There is the same kind of issue with io-ts where the schema inferred is wrong and can't even be passed to validate function when transforming the input data to a different one (example transforming a string to a Date object)

One solution is to remove the utility type Schema<T> and instead change the signature of validate and assert functions.

export async function validate<Schema>(
  schema: Schema,
  data: unknown,
): Promise<{data: InferOutput<Schema>} | {issues: Array<ValidationIssue>}> {
  const wrappedSchema = wrapCached(schema) ?? (await wrap(schema));
  return wrappedSchema.validate(data);
}

and:

export async function assert<Schema>(schema: Schema, data: unknown): Promise<InferOutput<Schema>> {
  const result = await validate(schema, data);
  if ('issues' in result) {
    throw result.issues[0];
  }
  return result.data;
}

I'm creating a PR to solve this in this way.
It will be a BREAKING CHANGE. Since you'll no more have Schema<T> type.
But i don't thing it's that bad, since nobody should use this utility type directly, because you don't want to write the ts type youself but always want to infer it.

TypeBox Optimization

@decs Hi, cool project :)

Hey, had just noticed TB got added to the supported list (which is awesome, thanks!). Was just looking over the implementation of the TB compiler though, and note there might be a couple of optimizations you could implement there to help ensure good validation performance. Was looking specifically at the following lines https://github.com/decs/typeschema/blob/main/src/adapters/typebox.ts#L36-L48

validate: async data => {
  const {TypeCompiler} = await import('@sinclair/typebox/compiler');
  const result = TypeCompiler.Compile(schema); // quite slow per validation pass
  if (result.Check(data)) {
    return {data};
  }
  return {
    issues: [...result.Errors(data)].map(
      ({message, path}) => new ValidationIssue(message, [path]),
    ),
  };
},

Two options for optimization below.

Value.Check

You can use the Value module to perform dynamic type checking against a value. This isn't as fast as the compiled version, but would be faster than compiling the schematics each time .validate(data) is run. The Value.Check performance is roughly inline with Zod.

validate: async data => {
  const {Value} = await import('@sinclair/typebox/value');
  if (Value.Check(schema, data)) {
    return {data};
  }
  return {
    issues: [...Value.Errors(schema, data)].map(
      ({message, path}) => new ValidationIssue(message, [path]),
    ),
  };
},

Cache by Schema

Another approach you can maybe try is to cache the compiled result by schema reference. This caching pattern is an alternative to closure caching (where you have the outer closure compile, and return a validate function), but can be used to retain previously compiled compilation and only incurs a cost of a single Map lookup.

const cache = new Map<TSchema, TypeCheck<any>>() // caches by reference

// ... 

validate: async data => {
  const {TypeCompiler} = await import('@sinclair/typebox/compiler');
  const result = cache.has(schema) ? cache.get(schema)! : (function() {
     const typecheck = TypeCompiler.Compile(schema);
     cache.set(schema, typecheck) // cache for next time.
     return typecheck
  }())
  if (result.Check(data)) {
    return {data};
  }
  return {
    issues: [...result.Errors(data)].map(
      ({message, path}) => new ValidationIssue(message, [path]),
    ),
  };
},

TypeBox supports both dynamic and compiled type checking. The decision to use one over the other mostly comes down to whether or not the environment is supportive of JIT compilation. For validation that runs everywhere, use Value.Check, for high performance where you have control over the environment, use the TypeCompiler.

Hope this helps, and great work. This project looks cool
Cheers
S

Beyond validation - introspecting the schema

Hi, thank you for a nice initiative to standardize a very fragmented area of the ecosystem. I'd like to get some ideas about how to build upon and improve the simple and useful API you have right now.

I'd love to use typeschema with Superforms, and in the future v2.0 be able to hook into any validation library. Right now only Zod is supported, which wasn't a random pick, it's a great library but also pretty much the only one I could find that goes beyond validation and has some important features for a comprehensive form library like Superforms.

You can read the details here: ciscoheat/sveltekit-superforms#120, but in essence I'm looking for a couple of additional things for a validation library:

A. Being able to introspect the schema types, to figure out the following about a schema field:

  • its type (the real or "underlying" type, since it can be hidden behind wrappers as in Zod with ZodEffect)
  • if its optional
  • if its nullable
  • its default value, if it exists

This allows Superforms to create default values for a schema, so you can do something like:

const form = await superValidate(schema);

Which will amongst other return an object with default values for the schema, so type-safety is ensured.

Another piece of data returned from superValidate is constraints, which takes us into the next level of introspection:

B. Introspect the specific validators on a schema field.

Given a Zod schema like this:

z.string().min(3).regex(/^\w+$/)

You can look into all validators and generate html validation constraints based upon them, so in this case:

{
  min: 3,
  pattern: '^\w+$'
  required: true
}

This object can then be spread on an html input field, and we have browser validation without requiring javascript.

I'm not really up to speed with the other libraries, so my question is, do you know if some of them have the possibility to handle A and B? A feature matrix would be awesome, then I can make a decision if it's worth pursuing this for v2.0.

It depends on how many libraries can handle case A basically. B is nice to have, but A is pretty much required, unless the library supports default values and the consumer want to supply their own for every field, which is a bit tedious. So maybe the matrix would be:

Project Default values A. Type introspection B. Validator introspection
zod โœ… โœ… โœ…
yup
joi
ajv
superstruct
io-ts
ow
typebox
typia
deepkit
runtypes
arktype

Let me know what you think, thanks for any help you can give. :)

Assert only throws the first error

Assert is only throwing the first issue atm.

I propose either target ES2021 to have AggregateError available. Or create your own to allow all errors to be reported :

export async function assert<TSchema extends RegistryBaseSchema>(
  schema: TSchema,
  data: unknown,
): Promise<InferOutput<TSchema>> {
  const result = await validate(schema, data);
  if ('issues' in result) {
    throw new AggregateError(result.issues);
  }
  return result.data;
}

You might be interrested to take a look at typematic

Hello,

Someone showed me your project. it's really interresting.

I did something a little different with my library Typematic : https://github.com/ts-api-spec/typematic

it's using some Higher Kinded Types trick to not tie the OpenApi Spec to any validation library at compile time/runtime.

The advantages over the tRPC approach :

  • someone else can implement an adapter without modifiying the core library or the core library being aware of the validation functions used in those libraries
  • allows to validate schemas that don't have a validate function inlined in the schema (like @effect/schema, json-schema or json-types)
  • allows to have errors properly typed
  • there is no clashing name possible (example: one lib using validate() to not throw while another lib do)
  • i have yet to find a library where this approach don't work

Some disadvantages :

  • you need to declare your type adapter somewhere in your document format to tell it what adapter to use (for example in your form decl, or your api spec decl). Meaning you need some form of integration for each use case. Typematic does it by being a declaration for api spec and you can set your adapter (called ShemaType) at any stage of the document, so you can still mix different validation libraries in the same document.

Maybe typematic can give you some ideas on how to have your cake and it up too.

Keep it up!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.