[HN Gopher] Designing the perfect TypeScript schema validation l... ___________________________________________________________________ Designing the perfect TypeScript schema validation library Author : colinmcd Score : 27 points Date : 2020-03-12 21:07 UTC (1 hours ago) (HTM) web link (vriad.com) (TXT) w3m dump (vriad.com) | adriancooney wrote: | Can't seem to spot a link to Zod's code Colin. Fancy making it a | bit more obvious in the post? | | Edit: Found it: https://github.com/vriad/zod | colinmcd wrote: | Just made it _significantly_ more obvious...Thanks for pointing | that out! | random_savv wrote: | There is also class-validator, which I've used and I find elegant | and working well: | | https://github.com/typestack/class-validator | colinmcd wrote: | Huh, not sure how I missed this. | | I don't love the class-based declarations: it's a bit verbose | and doesn't allow you to use things like the spread operator to | "mix in" fields into objects. There's also some redundancy | required for basic types: @IsString | firstName: string; | | For my purposes, I also needed support for recursive types, | unions, and intersections, which I don't believe are supported. | | But their validation built-ins go way beyond Zod (IsEmail, Min, | Max, Contains, native Date support, etc). Thanks for sharing. | sakagami0 wrote: | Wow I made the same thing that I put v1 up a couple days ago. | | https://github.com/tetranoir/presi | | A main difference is I tried to get rid of having a separate line | to create the Type and I tried to get rid of having to learn a | new libary's api as much as possible. | eyelidlessness wrote: | This looks interesting too, but the syntax fells _really weird_ | to me. | chris_st wrote: | Possibly stupid question, but why does the dog array validate: | const dogsList = z.array(dogSchema); dogSchema.parse([ | { name: 'Cujo', neutered: null }, { name: 'Fido', age: 4, | neutered: true }, ]); // passes | | Since 'Cujo' doesn't have an age? Assuming it's the same | dogSchema as in the previous block, age is required, right? | | Oh, and a minor typo: _This lets you confidently This way you can | confidently..._. | colinmcd wrote: | Whoops! Should be `dogsList.parse(...)`. | | Also fixed the other typo :) | | Thanks!! | andy_ppp wrote: | Sorry to be a pain but even a few rules (even resetting some | things) would make it much easier to read on mobile! | tobr wrote: | I don't want to derail the thread, but just a heads up: the | layout is very broken on my phone (iPhone). The text is | clipped on the left side which basically makes the article | unreadable! | andy_ppp wrote: | Haha! Timed that well didn't we... | Ezku wrote: | You mention creating object types with optional keys is | cumbersome in io-ts. How is that solved in zod, exactly? What | allows you to map `foo: union([bar, undefined])` to `foo?: bar | | undefined` (note the question mark on the left hand side)? | There's nothing in the declaration to give away why this wouldn't | yield `foo: bar | undefined` which is what I believe you'd get | out of io-ts. | | Looks useful - I would have an easier time introducing this than | io-ts. | colinmcd wrote: | Good question! It wasn't easy to get the question mark on the | left-hand side, but it is possible. | | Here's the Zod equivalent: const C = z.object({ | foo: z.string(), bar: z.number().optional(), | }); type C = t.TypeOf<typeof C>; /* { | foo: string; bar?: number | undefined } */ | | And here's the code that pulls this off: type | OptionalKeys<T extends z.ZodRawShape> = { [k in keyof | T]: undefined extends T[k]['_type'] ? k : never; }[keyof | T]; type RequiredKeys<T extends z.ZodRawShape> = | Exclude<keyof T, OptionalKeys<T>>; type ObjectType<T | extends z.ZodRawShape> = { [k in OptionalKeys<T>]?: | T[k]['_type']; } & { [k in RequiredKeys<T>]: | T[k]['_type'] }; export class ZodObject<T extends | z.ZodRawShape> extends z.ZodType< ObjectType<T>, // { | [k in keyof T]: T[k]['_type'] }, ZodObjectDef<T> | >{ // ... } | eyelidlessness wrote: | Looks pretty similar to how I did it with io-ts! I'm pretty | surprised that they don't support it by now. | eyelidlessness wrote: | With some effort, you can actually mix optionals and required | with io-ts as well. If I ever get permission to open source my | io-ts wrapper I'd be glad to show you how :) | colinmcd wrote: | I look forward to that! | | It's definitely possible to wrap `io-ts` to get a better | interface, especially if you spend some (or rather, a LOT) of | time figuring out the type declarations... | jrimbault wrote: | I'd really like something like a compiler step/plugin to generate | the validators from the declared typescript types. | dlbucci wrote: | Yeah, that's really something that feels like it should be a | part of the language, because it would be so useful! | eyelidlessness wrote: | It's explicitly one of the language's non-goals[1]. | | > Add or rely on run-time type information in programs, or | emit different code based on the results of the type system. | Instead, encourage programming patterns that do not require | run-time metadata. | | [1] https://github.com/Microsoft/TypeScript/wiki/TypeScript- | Desi... | eyelidlessness wrote: | This is basically that but in the opposite direction. It also | has the advantage of being able to express validations that you | can't express in the type system. | randomchars wrote: | Do you have an example of the kind of validation couldn't be | expressed in the type system? | root_axis wrote: | Min or max length of an input. Any type of string | validation like email or something similar that's validated | via regex. | eyelidlessness wrote: | root_axis gave some good examples. Also types like `int` or | `decimal` (which you can also pair with branded static | types, but they don't actually guarantee anything other | than their name. Really any kind of refinement you can | imagine on primitive types. | colinmcd wrote: | I should point out that there's nothing in Zod (yet) that | isn't expressible in Typescript. I'll eventually get around | to doing string/numerical validations (`.isEmail()`, etc) | that go beyond TS but currently all functionality has an | equivalent in TS. | colinmcd wrote: | You can generate JSON Schema from TS with this library: | https://github.com/YousefED/typescript-json-schema | | It's certainly conceivable to build an equivalent for Zod but | you'll eventually want to validate types that aren't | expressible in TS (i.e. Integer). | eyelidlessness wrote: | This is great. I recently built a fairly large amount of | functionality _around_ io-ts, adding support for mixed required | /optional fields, eliminating the need to deal with `Either` | monads, and a whole lot of other stuff that's mostly just | relevant to my company's usage. I think if this had been | available at the time I was looking for an underlying library, it | would have been a no brainer to choose this one. | | Worth noting, another option is Runtypes[1], which also looks | great. I can't remember off the top of my head why I ultimately | picked io-ts over Runtypes, but it's another one for folks to | consider (and I'd be curious what the Zod author thinks of it). | | https://github.com/pelotom/runtypes | eyelidlessness wrote: | Oh, I do want to add one bit of minor feedback for the author: | one of the things I like about the io-ts interface is that | fields/codecs are values unless they require a parameter (e.g. | `t.string` vs `z.string()`). It's a little bit easier for me to | read. It's also not clear to me at a glance whether calling | `z.string()` is creating a new instance of something and | whether that affects the behavior of a given schema. | colinmcd wrote: | I personally prefer to standardize everything as a function. | It also leaves some breathing room in case I decide to | include parameters as part of a future API augmentation. | colinmcd wrote: | Re: runtypes. Not sure how I missed this, it looks like an | excellent tool. | | No support for recursive types (which I personally want/need | for my project). | | I really like their API for constraint checking...might have to | steal that... | __michaelg wrote: | No mention of runtypes which looks pretty similar? | https://github.com/pelotom/runtypes | colinmcd wrote: | Huh. Totally missed this, it looks like an excellent tool. | | No support for recursive types (which I personally want/need | for my project). | | I really like their API for constraint checking...might have to | steal that... ___________________________________________________________________ (page generated 2020-03-12 23:00 UTC)