Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correct way to use a third party Class/type in a Zod schema object? #71

Closed
callumlocke opened this issue Jun 18, 2020 · 3 comments
Closed

Comments

@callumlocke
Copy link

I'm trying to do this, but TypeScript complains:

  z.object({
    slug: z.string().min(1).max(255),
    title: z.string().max(255),
    timeStart: Timestamp,
  })

Error:

Type 'typeof Timestamp' is missing the following properties from type 'ZodType<any, ZodTypeDef>': _type, _def, parse, is, and 5 more.

I basically want to make a schema that asserts that timeStart is an instance of the class Timestamp (which itself is a class I imported from a third party library).

How can I do this? Sorry if it's obvious.

Thank you for writing this library by the way, it's by far the best solution for TypeScript runtime checking I've found (and I've tried many).

@colinhacks
Copy link
Owner

colinhacks commented Jun 18, 2020

Hey Callum,

It's definitely not obvious. It may make sense to add a method specifically for this use case, I'll add it to the list.

In the meantime, it's already achievable but it's not super pretty:

const TimestampSchema: z.ZodType<Timestamp> = z.any().refine(x => x instanceof Timestamp);

z.any() lets anything through, but with the .refine makes sure that it's an instance.

You can "hard code" the type you want to validate by casting to z.ZodType<YourTypeHere>. Inference works as you'd expect:

type Timestamp = z.infer<typeof TimestampSchema>;
const time: Timestamp = new Timestamp(); // works fine

@callumlocke
Copy link
Author

Your suggestion using z.ZodType and .refine() works for me, thanks.

Yes, it's not super pretty at first glance. It does make sense now I see it, but it feels like a clever trick. I agree that something like z.instanceOf(Foo) would be a good addition for my specific use case. But I can see myself needing to use that z.any().refine(fn) trick again for other special cases. Maybe it would be good to provide a wrapper with a more intuitive name, like z.custom()? I guess it would also need to take a generic to set the type, something like:

const x: = z.custom<MyType>((value: any) => {
  // examine value and return true/false
}))

z.infer<x> // MyType

One more thing while I'm here: I think the name z.ZodType<T> might be better as z.Schema<T>, as you're using the word 'schema' throughout the readme. (Unless I've misunderstood what this utility is?)

Just to reiterate, I am in love with this library! Thank you for writing it. It's made writing TypeScript a joy.

@colinhacks
Copy link
Owner

Lots of great ideas here! Just added all three of these things to my todo list 🤙

I follow up here when they're implemented.

RobinTail added a commit to RobinTail/express-zod-api that referenced this issue Jan 10, 2024
I was dreaming about it.
Unfortunately `zod` does not provide any way to make a custom or branded
or third-party schema that can be identified as one programmatically.
Developer of `zod` also does not want to make any method to store
metadata in schemas, instead, recommends to wrap schemas in some other
structures, which is not suitable for the purposes of `express-zod-api`.
There are many small inconvenient things in making custom schema
classes, that I'd like to replace into native methods, and use
`withMeta` wrapper for storing proprietary identifier, so the generators
and walkers could still handle it.

Related issues:

```
colinhacks/zod#1718
colinhacks/zod#2413
colinhacks/zod#273
colinhacks/zod#71
colinhacks/zod#37
```

PR I've been waiting for months to merged (programmatically
distinguishable branding):

```
colinhacks/zod#2860
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants