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.22 Replies
Yes, I have "strict" and all other strict options set to true in tsconfig.json
Yeah, interesting, I can confirm in the most basic case: https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAbzgLzgXzgMyhEcBEyEAJvgNwBQFAxhAHYDO8D1AFgKYgCGcAvCgDoIAIwBW7ajAAUCCnDhcAXIK50AnlICUAGgppNlGvSZwAblwA2AV3Z84LDtwFguUBuxkLlAFgBM6AwoAeiD5eQA9AH4gA
TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
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.TS Playground - An online editor for exploring TypeScript and JavaS...
The Playground lets you write TypeScript or JavaScript online in a safe and sharable way.
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.(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
Yeah, I actually dug there myself 😄
This might be a limitation of the inference machinery we use.
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.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.Yeah definitely a breaking change
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)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
.Is it truly
any
or is it something like "a valid JSON value"?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 wantYeah, 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?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.
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!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 greatAs 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.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?Yeah, I have a feeling that this will be a bit of a long-running concern.