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

Require numbers to be finite by default? #1560

Open
Tyler-Murphy opened this issue Nov 14, 2022 · 9 comments
Open

Require numbers to be finite by default? #1560

Tyler-Murphy opened this issue Nov 14, 2022 · 9 comments

Comments

@Tyler-Murphy
Copy link

#1546 (closed #512) added a new way of specifying that numbers should be finite but didn't address the question of whether numbers should be required to be finite by default.

Would a pull request to make numbers finite by default be accepted? It would have to be a breaking change, but I think it would improve consistency and help prevent errors for most zod users.

From the previous issue:

I'd prefer that the default number implementation disallow non-finite values, so that z.number().parse(Infinity) throws an error. I think it'd be consistent with rejecting Number.NaN, since Infinity is a number-like thing in javascript that's not really a number, and NaN is already disallowed.

@igalklebanov
Copy link
Contributor

igalklebanov commented Nov 14, 2022

a. I'd argue that number()'s current behavior is unexpected, since NaN is of number type.

Imagine string() was not accepting empty strings for the very same reasons "because some people might forget to add .min(1)".

The explicit version .number().notNan() would be best. Closest to how people would approach it in plain javascript - typeof check -> Number.isNaN(...) check. Self-explanatory, intuitive, no implicit knowledge forcing users to go read the docs.

b. Most users use zod for user/external input validation, Infinity (and NaN) is not a valid JSON value, so no harm in "forgetting".

@Tyler-Murphy
Copy link
Author

I'd argue that number()'s current behavior is unexpected, since NaN is of number type.

I agree that it's unexpected, and I think that regardless of how finiteness is handled, NaN and Infinity should be treated consistently.

Imagine string() was not accepting empty strings for the very same reasons "because some people might forget to add .min(1)".

I think it's common for people to want an empty string, and rare for people to want NaN or Infinity. In my experience, almost no one considers NaN or Infinity even when they should, and it has caused unexpected errors many times. I think it makes more sense to have to opt in to using them.

Most users use zod for user/external input validation, Infinity (and NaN) is not a valid JSON value, so no harm in "forgetting".

That also means that this would mainly affect people doing math and validating the results. It wouldn't make a difference for most users, so no harm in adding the restriction.

I also want to note that in practice I've seen errors caused by external JSON where numbers larger than Number.MAX_VALUE were parsed and turned into Infinity, e.g. JSON.parse(Array(1000).fill('1').join('')). Such errors are rare, but I think preventing them is a good default behavior because almost no one expects them.

I guess this comes down to whether zod is for validating types or for validating semantic meaning, and to how important it is to protect people from themselves. I can't think of any other javascript types where a similar difference between type and semantic meaning exists, so it doesn't seem like there's a precedent.

Thanks for your thoughts, much appreciated!

@Tyler-Murphy
Copy link
Author

It looks like there is actually precedent for validating semantic meaning rather than javascript type. Invalid dates, which are similar to NaN and Infinity, are disallowed.

> z.date().parse(new Date('invalid'))
Uncaught:
[
  {
    "code": "invalid_date",
    "path": [],
    "message": "Invalid date"
  }
]

It seems like doing the same for numbers would improve library consistency.

@stale
Copy link

stale bot commented Mar 22, 2023

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.

@stale stale bot added the stale No activity in last 60 days label Mar 22, 2023
@Tyler-Murphy
Copy link
Author

Not stale

@stale stale bot removed the stale No activity in last 60 days label Mar 22, 2023
@stale
Copy link

stale bot commented Jun 20, 2023

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.

@stale stale bot added the stale No activity in last 60 days label Jun 20, 2023
@Tyler-Murphy
Copy link
Author

Not stale

@stale stale bot removed the stale No activity in last 60 days label Jun 22, 2023
@stale
Copy link

stale bot commented Sep 20, 2023

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.

@stale stale bot added the stale No activity in last 60 days label Sep 20, 2023
@Tyler-Murphy
Copy link
Author

not stale

@stale stale bot removed the stale No activity in last 60 days label Sep 20, 2023
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