-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Consider adding lenient()
option for string inputs
#804
Comments
Thanks for the write up and the thoughtful suggestion @nfantone ! Speaking for myself, I do not view Zod's main use case as parsing JSON or query strings but as safely turning an Your proposal ( Definitely open to continue discussing this and trying to support making this as easy as possible. FWIW, this is our numeric string schema: CodeSandbox const numericString = z
.string()
.refine((s) => {
const n = Number(s);
return Number.isFinite(n) && !Number.isNaN(n);
})
.transform(Number); |
Hi @scotttrinh. Thanks for your reply! You raise good points. Let me see if I can expand on them inline.
Well, at the risk of sounding a bit cheeky, saying that while having your core/main library function be named In all seriousness, I get what you mean here - but I really didn't want to circumscribe the uses of
It would a good place, absolutely. A better place?
Thanks for sharing that! That's really good. Doesn't quite fit my needs (i.e., the semantics of the
Of course, you can go ahead and try to fix those things. z.number().or(
z.preprocess(
value => (isNil(value) ? value : String(value).trim()),
z
.string()
.min(1, 'Expected number or numeric string, received empty string')
.refine(
s => {
const n = Number(s)
return Number.isFinite(n) && !Number.isNaN(n)
},
value => ({ message: `Expected number or numeric string, received '${value}'` })
)
.transform(Number)
)
) But I guess this kinda further proves the point I am trying to raise. |
😆
Yeah, I 100% agree with you that dealing with serialized data is something you have to do often. I guess my feeling is that Zod is an abstraction below that as a runtime type system. In a similar way to TypeScript, a
Totally fair! If Zod isn't the style of runtime type system that works for you, I think it's totally fine to say something like
On the contrary, I think that points to the point I'm trying to make: For us, we want to be absolutely certain that if something is a numeric string, it's a numeric string, not a date or I think this is why I feel like building a library (or having an internal module/library) is really the best way forward here: It allows Zod to focus on just representing and narrowing TypeScript types at runtime while providing affordances to do transformations/refinements/coercions as needed. This approach seems to be successful for As a separate note, here is another wrinkle in the proposed solution: And think of all of the other types beyond the primitives. What is a |
Ok, so I'm 100% behind everything you are saying here, in principle 👍🏼. Except (there's always a "but"), I have a small issue with the implicit implication that the original use case I provided for something like Since you brought up the topic of libraries, after seeing what other libraries closer to
I admit I didn't go deep into the implementation details of my proposal. But I suppose that |
Right, but as your example pointed out, you think the parser should accept numbers also, and if the string is empty it should throw an error. I think that's perfect valid, but that's not at all how I would want something similar to act. I think that's what I'm trying to say when I say that I think each developer (or team) needs to make decisions about how, when, and in what way serialized data in converted, and that providing functionality that picks a way is necessarily opinionated. I don't mean to be dismissive: I think your approach is a good one that makes sense for some use cases!
I 100% agree, and a lot of people have brought up other such use cases: especially forms. In each case, you might want to make different decisions about how to cast. For another example,
From my perspective, the schema for that ( I don't mean to come across as confrontational, and I very much appreciate your perspective and thoughtful answers and suggestions here. My hope is that users with the right vantage point based on their expertise and opinions can provide the layer that you feel we're missing, and I very much agree with you that the ecosystem is missing these sorts of developer-friendly and use-case specific libraries. I am also frustrated that I have to write these transforms by hand, but even if we provided your specific solution, I would still write them by hand since they do not align with my team's specific viewpoint on the proper way to specify these schemas for the multitude of use cases we have (json, query strings, form data, database data, etc.). I hope my comments help to situate my opinion (and that's all this is: my opinion!) about the direction I'd like to see Zod take and don't dissuade you from continuing to advocate for your own perspective. |
You're not coming across as confrontational - quite the contrary. Don't stress about it! And many thanks for taking the time to reply thoughtfully. And again, I do agree with your points, even if we don't see things exactly the same colour. Perhaps I'm expecting things from The one comment I would like to address is:
I would like to challenge that. IMHO, there are (several) issues with your suggestion. These are the ones I can think of off the top of my head.
So, no - sadly, I don't think it's straightforward in ** I appreciate your comments above on how this is "my own use case" and it points to |
I ran into this for a rather weird use case where we're currently using a system that "stringifys" all the properties passed in. So we get a correctly shaped object, but all the booleans/numbers/etc... are stringified. It's another edge case I'll admit, but it is a case. Of the above the most "significant" issue personally is the inability to chain. So even if I wanted to write my own "type" I end up having to go through contortions to make it look like a normal type. |
This is true for every design decision on every project, ever. Any and all additions to an existing stack bear forward a certain opinion, dismissing (voluntarily or not) others. I frankly don't see this being an argument against implementing new stuff. Also, I'm not convinced that the fact that there might be "different opinions" on how to convert data, prevents us from giving users the option. There are also different opinions on how to parse functions, objects and every other type out there and still const z = require('zod')
typeof null // 'object'
z.object().parse(null) // 'Expected object, received null' <--- Opinion 👀 |
I'd like to share what I've been experimenting with while working on a project to convert the outputs of openapi-typescript to Zod in order to implement a strongly typed REST API server. Parsing JSON bodies is straightforward. But, as already mentioned in the thread, parsing path, query, and header parameters is trickier because, although the Open API spec and Zod schemas may specify a parameter to be boolean-valued, numeric, etc., these parameters always arrive as strings. I've taken the approach of preprocessing my Zod schemas (ZodObjects) for handling these parameters. I do this by preprocessing each ZodObject property:
I give precedence to boolean values and then numeric values before falling back to whatever the ZodSchema is looking for (which hopefully can be parsed from a string). Although it's a bit annoying to do this, I am forced to take some decisions that may not be applicable to other use cases. I think I generally agree with @scotttrinh's comment here: #804 (comment) |
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. |
FWIW I'm encountering similar struggles when attempting to work with number inputs, using Given...
How do you represent a number input? |
Please see coercion: https://github.com/colinhacks/zod#coercion-for-primitives |
I was in the process of migrating my existing
yup
schemas tozod
and found that the main sticking point is handling the parsing/validation of request path and query string parameters. Since they are typically considered raw strings and the parsing is left to the application level,zod
doesn't really provide a great DX in these scenarios. Specially when compared to howyup
does it out-of-the-box.Which leads me to write custom
preprocess()
validators, along with custom error messages, for each expected type, every time. Here's an example for validating numerical strings.I know this has been discussed before, but having something close to a
.lenient()
parsing option, allowing for values to be internally coerced would be great.IMHO, this is such a common scenario when dealing with serialized data, that it only makes sense for a library such as this to support it without extra hassle. In addition, while the
.preprocess()
method above works, it transfers the responsibility of the parsing to the user, which is arguably the main use case ofzod
.The text was updated successfully, but these errors were encountered: