Z
Zod•15mo ago
bloberenober

Heya For some reason if I use `z any `

Heya! For some reason, if I use z.any(), z.unknown() or z.custom() as a value in z.object(), it's always inferred as optional, even though other values are correctly inferred as required. I even tried chaining .required() on the object, doesn't help. Couldn't find any hints in the docs about this behavior.
No description
No description
22 Replies
bloberenober
bloberenober•15mo ago
Yes, I have "strict" and all other strict options set to true in tsconfig.json
Scott Trinh
Scott Trinh•15mo ago
Looks like this is just the behavior of how we find which keys are required, and since any and unknown are assignable to undefined, we end up making them optional. I wouldn't say that's strictly intended, but it's strictly true in a sense. You should be able to get around it for custom by actually supplying a type.
bloberenober
bloberenober•15mo ago
Well, yes, but I actually need the value to be any, but required in the object. I just tried custom because it's any by default.
Scott Trinh
Scott Trinh•15mo ago
(if you're curious, here is the line in the code that adds question marks to the output type: https://github.com/colinhacks/zod/blob/51e14beeab2f469fcbf18e3df44653e1643f5487/src/helpers/util.ts#L108)
GitHub
zod/util.ts at 51e14beeab2f469fcbf18e3df44653e1643f5487 · colinhack...
TypeScript-first schema validation with static type inference - zod/util.ts at 51e14beeab2f469fcbf18e3df44653e1643f5487 · colinhacks/zod
bloberenober
bloberenober•15mo ago
Yeah, I actually dug there myself 😄
Scott Trinh
Scott Trinh•15mo ago
This might be a limitation of the inference machinery we use.
bloberenober
bloberenober•15mo ago
Honestly, this doesn't sound right - I looked at it again and thought it'll work the same way with z.undefined(), and indeed it is.
No description
Scott Trinh
Scott Trinh•15mo ago
Yeah, that mapped type will take any key that is possible to assign undefined to and make it optional, so that makes sense. I'm not saying that's a great result (and indeed I think it ought to not be automatic like this), but that's the current behavior. I suspect a change here is a breaking change.
bloberenober
bloberenober•15mo ago
Yeah definitely a breaking change
Scott Trinh
Scott Trinh•15mo ago
In your case, you can assert the type of the schema, i think. Lemme try well, no, that doesn't work. yeah, I think you're SOL if you want to have a key that you require to be defined but might have the value undefined (which, imo is a bit of a smell, but I get that it's useful in some cases)
bloberenober
bloberenober•15mo ago
My use case is I'm making a library that maps DB table schemas to Zod schemas, and if there's a column type that isn't supported it bails out as any.
Scott Trinh
Scott Trinh•15mo ago
Is it truly any or is it something like "a valid JSON value"?
bloberenober
bloberenober•15mo ago
Or (much more likely) if there's a json field without an assigned type, it's mapped to unknown, which also becomes an optional field. Well, it's not just a valid JSON value - it can be any value, really, since there can be columns with custom mappers which can accept value of any type they want
Scott Trinh
Scott Trinh•15mo ago
Yeah, I can see the appeal of making it unknown here so that the consumer needs to do a bunch of checking to be able to use it at runtime, I would likely reach for the same solution, but I think you can probably define a better bottom type here that is more accurate, even though it's an enormous pain. but "on the wire" it's not likely to be anything right? Or is this coming after some other process has already acted on it?
bloberenober
bloberenober•15mo ago
So imagine an ORM, which provides a possibility to create custom column types. When you define a custom column type, you can specify a custom mapper that'll map the value from the model to the value passed to the DB and vice versa. What it means is that in theory that column can accept literally anything as its value, and there's no way to map that behavior to a Zod schema in runtime, because the accepted type is only declared on a type level.
Scott Trinh
Scott Trinh•15mo ago
Yeah, I guess we'd want that "mapping" to happen in Zod rather than before, otherwise, yeah, unknown is the right type, but it could also remove the key, so maybe ?: unknown is also correct? But, absolutely agree that z.unknown() makes the most sense here and having it become optional without a way to opt-out is not great!
bloberenober
bloberenober•15mo ago
Yeah, basically it would mean that all unknowns in the object become optional on the type level, but required at runtime, which will cause users to constantly add ! to the value, which is far from great
Scott Trinh
Scott Trinh•15mo ago
As a consumer I would personally be annoyed that I wasn't able to define explicitly what type it should be and how to parse it, but unknown is better than nothing here.
bloberenober
bloberenober•15mo ago
I guess I can make a workaround for json/blob fields to map them to a union of all possible JSON types, so this'll be less painful But still, the underlying issue is that the any/unknown keys in the object shouldn't be optional in the first place. I'm wondering if there's any way to fix that behavior? I understand that it's a breaking change so it can't be included in a next release, but is there maybe a next major beta that can include such fixes?
Scott Trinh
Scott Trinh•15mo ago
Yeah, I have a feeling that this will be a bit of a long-running concern.