Open kibertoad opened 8 months ago
I would take this a step further and allow the "coerce" function to be specified instead of allowing only a default per type. E.g. for arrays, a "coerce" could split an input string by ",".
This feels very specific to me, and doesn't strike me as general-purpose API that would be widely used. Solving this with .preprocess
is exactly what I'd recommend here.
@colinhacks Is it? For any webservice that relies on zod for defining request schemas this is a very common case for handling request query params, when array value arrives with a single entry, and hence web framework has no way to determining on its own whether it's a string or an array of strings
I am using something like this in the meantime if anybody is interested (there is certainly room for improvement):
const asArray = <T extends ZodTypeAny>(type: T) => z.any().transform<z.infer<typeof type>[]>((value: any) => Array.isArray(value) ? value.map(el => type.parse(el)) : [type.parse(value)]);
const asSingle = <T extends ZodTypeAny>(type: T) => z.any().transform<z.infer<typeof type>>((value: any) => type.parse(Array.isArray(value) && value.length > 0 ? value[0] : value));
const zMyParams = z.object({
foo: asArray(z.string()),
bar: asArray(z.coerce.number()),
baz: asSingle(z.string()),
qux: asSingle(z.coerce.number())
});
type MyParams = z.output<typeof zMyParams>;
// gives:
// type MyParams = {
// foo: string[];
// bar: number[];
// baz: string;
// qux: number;
// }
console.log(zMyParams.parse({
foo: ['one', 'two'],
bar: ['1', '2'],
baz: ['one', 'two'],
qux: ['1', '2']
}));
// gives:
// {
// "foo": ["one", "two"],
// "bar": [1, 2],
// "baz": "one",
// "qux": 1
// }
console.log(zMyParams.parse({
foo: 'one',
bar: '1',
baz: 'one',
qux: '1'
}));
// gives:
// {
// "foo": ["one"],
// "bar": [1],
// "baz": "one",
// "qux": 1
// }
This feels very specific to me, and doesn't strike me as general-purpose API that would be widely used. Solving this with
.preprocess
is exactly what I'd recommend here.
Tinkering with this earlier, I would agree, and it works as intended, however in some cases types resulted in "any" or "unknown" Which is problematic. An example here might be a good thing.
Currently there is no way to tell Zod "if you get a single value of correct type, just put it into an array". Existing
.coerce()
operation could be used for this.Here is a reference implementation of this logic: https://github.com/lokalise/zod-extras/blob/main/src/utils/toArrayPreprocessor.ts Test: https://github.com/lokalise/zod-extras/blob/main/src/utils/toArrayPreprocessor.test.ts