11# Tiny Schema Validator
22
3+ small, practical, and type-safe Schema validator.
4+
35[ ![ GitHub license] ( https://img.shields.io/github/license/5alidz/tiny-schema-validator )] ( https://github.com/5alidz/tiny-schema-validator/blob/master/LICENSE ) ![ Minzipped size] ( https://img.shields.io/bundlephobia/minzip/tiny-schema-validator.svg )
46
5- - installation
6- - usage
7- - schema
8- - validators
9- - advanced usage
10- - caveats
7+ ## History
8+
9+ This started as a side-project for me to learn about advanced TypeScript topics and was never intended to be an npm package,
10+ but I liked how it turned up and decided that it might be useful to use in my future projects.
1111
1212## Installation
1313
@@ -33,29 +33,18 @@ export const Person = createSchema({
3333});
3434```
3535
36- and in TypeScript
36+ and in TypeScript everything is the same, but to get the data type inferred from the schema you can do this:
3737
3838``` ts
39- import { createSchema , _ } from ' tiny-schema-validator' ;
40-
41- interface IPerson {
42- name: string ;
43- age: number ;
44- email: string ;
45- }
46-
47- export const Person = createSchema <IPerson >({
48- name: _ .string ({ maxLength: [100 , ' too-long' ], minLength: [2 , ' too-short' ] }),
49- age: _ .number ({ max: [150 , ' too-old' ], min: [13 , ' too-young' ]}),
50- email: _ .string ({ pattern: [/ ^ [^ @] + @[^ @] + \. [^ @] + $ / , ' invalid-email' ]});
51- });
39+ // PersonType { name: string; age: number; email: string; }
40+ export type PersonType = ReturnType <typeof Person .produce >;
5241```
5342
5443## Schema
5544
5645When you create a schema, you will get a nice API to handle multiple use-cases in the client and the server.
5746
58- - ` is(data: any): boolean ` check if the data is valid
47+ - ` is(data: any): boolean ` check if the data is valid (eager evaluation)
5948- ` validate(data: any): Errors ` errors returned has the same shape as the schema you defined (does not throw)
6049- ` produce(data: any): data ` throws an error when the data is invalid. otherwise, it returns data
6150- ` embed(config?: { optional: boolean }) ` embeds the schema in other schemas
@@ -70,7 +59,7 @@ Person.is({ name: 'john', age: 42, email: '
[email protected] ' }); // true
7059Person .validate ({}); // { name: 'invalid-type', age: 'invalid-type', email: 'invalid-type' }
7160Person .
validate ({ name
: ' john' , age
: 42 , email
: ' [email protected] ' });
// null7261
73- Person .produce (undefined ); // throws an error with the same shape as the schema
62+ Person .produce (undefined ); // throws { name: 'invalid-type' }
7463
7564// embedding the person schema
7665const GroupOfPeople = createSchema ({
@@ -127,26 +116,27 @@ Check out the full validators API below:
127116
128117### Custom validators
129118
130- You can use validators from ` _ ` as building blocks for your custom validator:
119+ To create custom validators that does not break type inference:
120+
121+ - use validators from ` _ ` as building blocks for your custom validator.
122+ - your custom validator should define an ` optional ` and ` required ` functions.
123+
124+ Example of creating custom validators:
131125
132126``` js
133- const alphaNumeric = validatorOpts =>
134- _ . string ( {
127+ const alphaNumeric = (() => {
128+ const config = {
135129 pattern: [/ ^ [a-zA-Z0-9 ] * $ / , ' only-letters-and-number' ],
136- ... validatorOpts,
137- });
138-
139- const email = validatorOpts =>
140- _ .string ({
141- pattern: [/ ^ [^ @] + @[^ @] + \. [^ @] + $ / , ' invalid-email' ],
142- ... validatorOpts,
143- });
130+ };
131+ return {
132+ required : additional => _ .string ({ ... additional, ... config, optional: false }), // inferred as Required
133+ optional : additional => _ .string ({ ... additional, ... config, optional: true }), // inferred as Optional
134+ };
135+ })();
144136
145137const Person = createSchema ({
146138 // ...
147- username: alphaNumeric ({ maxLength: [20 , ' username-too-long' ] }),
148- email: email (),
149- alt_email: email ({ optional: true }),
139+ username: alphaNumeric .required ({ maxLength: [20 , ' username-too-long' ] }),
150140 // ...
151141});
152142```
@@ -169,14 +159,16 @@ const User = createSchema({
169159});
170160
171161const form_ui = User .traverse ({
172- number (path , key ) {
162+ number ({ path, key } ) {
173163 if (path .includes (' profile' )) return { type: ' number' , label: key };
174164 return null ; // otherwise ignore
175165 },
176- string (path , key ) {
166+ string ({ path, key } ) {
177167 if (path .includes (' profile' )) return { type: ' text' , label: key };
178168 return null ; // otherwise ignore
179169 },
170+ // this is required to get the type of "profile" correct
171+ record : () => null ,
180172});
181173
182174console .log (form_ui); /*
@@ -197,9 +189,7 @@ The return type of your visitor is important, and there are a few considerations
197189Returning ` null ` from visitor signals to ignore this node from the result, with the exception:
198190` record | recordof | list | listof ` , returning ` null ` signals to continue down recursively.
199191
200- So to return something from ` record ` visitor you will need to visit its children recursively.
201-
202- _ Note_ : in most cases defining only the primitive visitors is enough.
192+ So to return something from ` record ` visitor for example, you will need to visit its children recursively.
203193
204194Continuing from the previous ` User ` Example
205195
@@ -231,8 +221,8 @@ const customTraverse = (key, validator) => {
231221};
232222
233223const form_ui = User .traverse ({
234- record (path , key , recordValidator ) {
235- return customTraverse (key, recordValidator );
224+ record ({ path, key, validator } ) {
225+ return customTraverse (key, validator );
236226 },
237227});
238228```
@@ -252,3 +242,7 @@ _.list([_.number({ optional: true /* THIS IS IGNORED */ }), _.number()]);
252242const list = createSchema ({ list: _ .listof (_ .string ()) });
253243list .validate ({ list: [' string' , 42 , ' string' ] }); // { list: { 1: 'invalid-type' } }
254244```
245+
246+ ## Recursive types
247+
248+ Currently there's no easy way to create recursive types, if you think you could help, PRs are welcome
0 commit comments