[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)