I highly recommend reading this with full Typescript support in the code-snippets
Headless Type-safe validation library for Javascript
As with other validation libaries like zod
, yup
, io-ts
, superstruct
, valibot
, etc...
You can easily validate any value using:
Loading...
What this library does differently is that it puts the focus on customization and makes the core so simple,
that you can define your string
validator like this:
Loading...
This is actually how the string
from the initial example is defined.
Why a new library?
I've written a lengthy article about the problems I've encountered while trying to validate a simple form, and not wanting to get forced into some custom strane DSL.
It's a good read, I promise.
The gist of it is that validating a value as string
or number
is easy in any of the aforementioned libraries.
What's not easy, is validating an IBAN using validator.js
, or validating an IBAN, but showing different error messages for countries whose IBAN your
SaaS doesn't currently support, but will support in the next quarter.
If you want to do this in zod
, you'll have to understand the difference between
refine
https://zod.dev/?id=refine
superRefine
https://zod.dev/?id=superrefine
transform
https://zod.dev/?id=transform
You'll also encounters lots of interesting issues related to how these 3 methods combine with each other.
Type for the errors
The type of which type of "errors" you can receive is usually not enforced.
This library takes a different approach and considers errors as first class citizens.
That was the main reason the function bad
isn't called error
since it's not something I like to throw, it's something I want to know and understand.
The core library in this:
./esm/core.js
Loading...
Everything else is based on the core types and those 4 functions.
Custom validators:
If you want to validate that something is an IBAN you can just define a function:
Loading...
Common utilities:
Of course, there are the basic utilities, for things you'd expect from other type-safe libraries:
primitives
/packages/sure/esm/primitives.js
The library doesn't provide too many primitives, the idea being, that you already know how to check if something is a string.
Other libraries have lots of different views about what a string is (empty or not), or what a number is (NaN or infinity).
Usually I want to validate if something is a positive integer, or if something is a valid age. In that case I just write a function, that's all.
Nevertheless, there are currently several primitives:
Loading...
object
and optional
Loading...
array
Loading...
after
This is the refine
function from zod
, but it's much simpler to use.
It runs the first validator, and if it's successful, it runs the second validator.
It returns the first bad value it encounters.
Loading...
Other utilities
tuple
Loading...
literal
Loading...
union
= or
Loading...
Advanced utilities
intersection
= and
/packages/sure/esm/intersection.js
Currently and
(which is a different name for intersection
) works only on objects, since I use object destructuring when returning
the final value.
Loading...
recursive
The recursive
function is a bit more complex, basically, you have an object and you can say that one of the fields is expected to be recursive.
Afterwards you can use the recurse
function to define the shape of the recursive element.
The idea here is that it's not possible to know the shape of the recursive element before you define the object that contains it.
This was implemented mostly to test the limits of the library.
Loading...
The meta
property in the validation function
Things like object
that return another function which is the actual validator, can have data that's attached to them.
At the moment I don't personally use this feature.
They were added to allow introspection of the validation schema in cases where it might be necessary to
Decided how to store any metadata was a tought decision, especially when validators are simple functions.
The meta
is a property that can be directly set to a function.
Loading...
This type tells us that a function can either NOT have a meta
property, or it can have meta
property that you don't know the type of.
This idea can be applied to any function whatsoever, since any function can either have a meta
property or not. Most functions don't.
This seemed like the less invasive option.