Skip to content
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

Creating a z.function() with no arguments does not produce a TS type with no arguments #3947

Open
0-CAT opened this issue Jan 21, 2025 · 2 comments

Comments

@0-CAT
Copy link

0-CAT commented Jan 21, 2025

Summary

Primitives in JS (TS) are compatible with a type which defines { toString: () => string }. However, if a Zod type is created using z.function() with no arguments, the generated types have an ...args parameter that prevent it from being used with primitives. (That is, primitives are not compatible with { toString: (...args: unknown[]) => string }.)

I believe if you call z.function() without providing an args() call, the produced matcher should not have an ...args parameter.

Details

If I do the following:

const toStringSchema = z.object({
	toString: z.function().returns(z.string()),
});

type ToStringAble = z.infer<typeof toStringSchema>;

const foo: ToStringAble = 4;

I get the following error from the final line: ts: Type 'number' is not assignable to type '{ toString: (...args: unknown[]) => string; }'.

Image

However, if I remove Zod from the picture and define a type manually which I would expect to be equivalent, it works:

Image

This is because the defined type does not have an ...args parameter.

@kockar96
Copy link

Hmm, this is interesting. Tbh, not sure if this can be done 😄
What do you want to achieve?
If you want to have some runtime validation that something has toString you can do something like
const toStringSchema = z.any().refine(
(val) => typeof val?.toString === "function",
{ message: "Value must have a toString method" }
);
This does not provide a type safety but it works.

@0-CAT
Copy link
Author

0-CAT commented Jan 25, 2025

What I'm looking to achieve is to make sure that the function provides a type definition with a function that takes no arguments, rather than a variable-length parameter array. I think it's a very unique use case, though—having to match the toString function is definitely a very rare requirement. 😅

I guess for this use case, I could just override the type definition to get what I need.

const toStringSchema = z.any().refine(
  (val) => typeof val?.toString === "function",
  { message: "Value must have a toString method" }
);

type ToString = Omit<z.infer<typeof toStringSchema>, 'toString'> & { toString: () => string };

Would be interesting to see if this could be done, though. Maybe with a different type, like z.function().noArgs()?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants