← Back to context

Comment by Xenoamorphous

5 hours ago

I don't like zod. I want to define my types, not write schemas. And I don't like that then I have to use the types derived from those schemas rather than types I've defined myself directly.

So I just define my types and then use typescript-json-schema or similar to build a JSON Schema at build time (i.e. from an npm script) which then I use to validate input using ajv.

The only thing I do on top of that is to use annotations like "@minimum 0" (or, in the email example, "@format email") where the base types are not enough, but those simply go inside comments.

So the compiled package only has ajv as runtime dependency (which you're likely to have anyway, as it's everywhere), you're just defining regular types with some annotations on top and use a dev dependency to build you the JSON Schema. And as popular as zod is, I think JSON Schema is more of a standard and likely to stay with us longer.

I also reference those generated JSON Schemas from my OpenAPI definition, as a bonus.

While I would much prefer to only write Typescript types, this would drive me insane:

> The only thing I do on top of that is to use annotations like "@minimum 0" (or, in the email example, "@format email") where the base types are not enough, but those simply go inside comments.

  • Obviously it's not ideal, but IMO it's the better option. Much better than `z.number().integer().min(0)` or whatever zod equivalent there is and then have to deal with the inferred types which among other things tend to suck for IntelliSense etc. Those annotations map directly to JSON Schema attributes.

    • You can AOT typescript types from zod schemas if intellisense is your main complaint. I think it generally makes more sense to transform from more expressive => less expressive, and zod is more expressive than typescript (as evidenced by your need to add doc comment annotations to get similar behavior going the other way).

I think this misses what is undeniably the best part about zod. Yes, you could define all of your types this way, but it’s only necessary at the boundary of the program. Internal functions don’t need to validate inputs if the caller is trusted.

Being able to define a loose input schema at the boundary and then transform it into a shape that your program actually needs is extremely useful.

What you're doing is essentially what Zod is designed to avoid. If you tolerate needing a separate build step more than having to define types with Zod's syntax, then it makes sense not to use Zod since it's not made for you.

  • To me the build step is a good thing. It's a simple script in npm, and it means I only keep what I need (the JSON Schema, which I don't need at dev time) in runtime and whatever package generates those schemas out of TS types can remain as a dev dependency.

    zod can't be a dev only dependency, and you have to deal with breaking changes and maybe switching to a completely different library in a few years (joi, with a syntax very similar to zod's, was very popular a while ago too).

    • How would you use zod as a dev-only dependency? The whole point there is to be a parser. If you remove it from runtime then you are not parsing the production payloads, which is the exact place where you do need to parse them.

This is what I also do not just in JS but also in other languages. But I write the schemas. And I dont use TS. Im glad Im not the only one. The OP post gave me a serious headache trying to read it.

Parse and Validate are not binary choices and have nothing to do with each other. Both are useful when applied correctly to a given situation.

I felt punked by most of it. I dont see what programming languages have to do with it either. Look at swift, a language that can barely only barely parse JSON. Who cares?

For all its faults, this is one of the things the Python typing system gets right. It's dynamically introspectable at runtime, so you can define type, parsing and validation in one go with stuff like pydantic.