zod
zod copied to clipboard
variadic args function
I am trying to define function of this shape:
return z
.function()
.args(...args , makeAPISchema(valueSchema))
.returns(returns)
where makeAPISChema returns a z.object but it keeps throwing the following error:
Variadic element at position 0 in source does not match element at position 0 in target.
Is there a way to make this work?
You can use the .rest method on ZodTuple to add variadic arguments to a function schema.
z.function()
.args(z.tuple([z.string()]).rest(z.number()))
.returns(z.number())
.implement((args) => {
return args[1]; // [string, ...number[]]
});
@colinhacks, I don't really feel like this issue was resolved. Can you let me know what I'm doing wrong?
const foo = z.function()
.args(z.tuple([z.string()]).rest(z.number()))
.returns(z.number())
.implement((args) => {
return args[1] // [string, ...number[]]
})
console.log(
foo('bar',1,2,3)
// ^^^^^
// Argument of type 'string' is not assignable to parameter of type '[string, ...number[]]'.
)
@JacobWeisenburger you're correct - the suggestion with z.tuple() makes it so we expect an array with the original parameters which is likely not what @hyusetiawan intends.
I think here unfortunately there's a "limitation" in the sense that the expected explicit type is '[] | [ZodTypeAny, ...ZodTypeAny[]]' from ZodTuple and seems foundational for the type. The issue here is a spread operator without explicit typing ends up being of the form ZodTypeAny[] and that is not compatible (the other type requires at least one explicit element)
type requiredType = [ZodTypeAny, ...ZodTypeAny[]];
const wellTyped: [z.ZodString, ...z.ZodString[]] = [z.string(), z.string()];
const untyped = [z.string(), z.string()];
const tryFirst: requiredType = wellTyped; // OK
const trySecond: requiredType = untyped; // error: Type 'ZodString[]' is not assignable to type 'requiredType'.
we could try expanding the definitions on ZodTuple but it seems more work than just adding a couple of definitions. I think in theory spread should "just work" but as I see it:
- a user can use spread, but needs to type the array being spread to guarantee at least one element or none to satisfy the current type
- we can look at rejigging the types in
ZodTuplebut seems awkward given the way one has to declare "at least one element in array" specific types
// this works
const possibleInputs: [] | [z.ZodString, ...Array<z.ZodString>] = [
z.string(),
z.string(),
];
const foo = z
.function()
// .args()
.args(...possibleInputs)
.returns(z.string())
.implement((args) => {
return args[0];
});
console.log(foo("bar", "faz"));
Open to other ideas!
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.