(Translated with www.DeepL.com/Translator. Original Japanese version is https://zenn.dev/mizchi/articles/lizod-is-lightweight-zod)
lightweight-zod, hence lizod
.
You can use npm install lizod -S
.
tl;dr
- The build size of zod is getting in the way of various frontends and Cloudflare Workers.
- I’ve created a zod-like validator that discards the method chain and all the useful utilities.
- lizod is less than 1kb compared to zod’s 57kb
It works.
// Pick validators for treeshake
import {
$any,
$array,
$boolean,
$const,
$enum,
$intersection,
$null,
$number,
$object,
$opt,
$regexp,
$string,
$symbol,
$undefined,
$union,
$void,
type Infer,
type Validator,
} from "lizod";
const validate = $object({
name: $string,
age: $number,
familyName: $opt($string),
abc: $enum(["a" as const, "b" as const, "c" as const]),
nested: $object({
age: $number,
}),
static: $const("static"),
items: $array($object({
a: $string,
b: $boolean,
})),
complex: $array($union([
$object({ a: $string }),
$object({ b: $number }),
])),
sec: $intersection([$string, $const("x")]),
});
const v: Infer<typeof validate> = {
name: "aaa",
age: 1,
familyName: null,
abc: "b",
nested: {
age: 1,
},
static: "static",
items: [
{
a: "",
b: true,
},
{
a: "",
b: false,
},
],
complex: [
{ a: "" },
{ b: 1 },
],
sec: "x",
};
if (validate(v)) {
const _: string = v.name;
const __: number = v.age;
const ___: string | void = v.familyName;
const ____: "a" | "b" | "c" = v.abc;
const _____: { age: number } = v.nested;
const ______: "static" = v.static;
const _______: Array<{
a: string;
b: boolean;
}> = v.items;
}
Why I made it
Recently, there is a high demand to run zod together with remix and others under the 1MB limit of Cloudflare Workers (5MB for paid plans). 220kb of remix is unavoidable since it is a framework, but zod has a size of 57kb. 57kb for a single function validator is a little bit too much.
https://bundlephobia.com/package/zod@3.21.4
bundlephobia says 57kb, but I checked it with my own build and it was about 43kb. However, it is still big.
I need a lightweight zod alternative for Cloudflare Workers, not to mention the build size of the frontend.
So, I made what I think is the best zod alternative.
Why is zod size so big?
In general, libraries that adopt the API style of method chaining do not benefit from the ESM + various bundlers to remove unnecessary code. Imagine jQuery, for example.
Validators such as zod do not need to be method chains and could be represented by function composition, but the Pipeline Operator that allows this to be written naturally is still being hotly debated at Stage 2 and is not expected to be available anytime soon.
https://github.com/tc39/proposal-pipeline-operator
As far as I read the Zod code, there is a lack of optimization for tree shaking by method chaining, and the Locale definition for displaying Error Reporter and Error Message is quite large.
I thought about Forking Zod itself, but I thought I could achieve my goal by implementing the parts I really want in my use case in the way I want.
Features I want in zod
- A set of validators at the same level as TypeScript type expressions
- An
Infer
to the synthesized validators
So, I first created it with this kind of API set in mind
import { $object, $array, $union, $number, $string, type Infer } from "lizod";.
const validate = $object({
v: $array($union($string, $number))
});
type MyType = Infer<typeof validate>
const input: any = {
v: [1, "hello", "world", 9]
}
if (validate(input)) {
// type narrowing in this scope
const v: Array<number | string> = input.v
}
zod does not mesh with TypeScript type expressions in some places due to the merging of existing validators and instruction sets. lizod can be used with the same type sense as TypeScript’s A | B
and A & B
since the instruction levels are $union
and $intersect
.
Things you don’t need
- Utilities like
email()
. It implements$regexp(expr)
instead, so the regular expression itself is brought separately.- One of the reasons why zod is so heavy is because it wraps a lot of this kind of regexp inside…
- Method Chain
- Clever Error Reporter
In the spirit of “create your own missing validators”, only primitive validators are implemented in the main body.
Finally
I think there are many people who think that zod is only possible with a collection of utilities, or that not having an error reporter is a shame, etc. For those people, lizod is a great tool. For those people, lizod’s small amount of code may be useful as a foundation to fork and tweak.
At least, I expect this kind of functionality from zod, and when I implemented it, it became lizod. I have been able to create it because I have been getting more and more good at type-puzzling recently.
I am waiting for your comments, feature suggestions, and PR.