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

.nullable and .default #425

Closed
mariusmarais opened this issue May 5, 2021 · 3 comments
Closed

.nullable and .default #425

mariusmarais opened this issue May 5, 2021 · 3 comments
Labels
wontfix This will not be worked on

Comments

@mariusmarais
Copy link

mariusmarais commented May 5, 2021

EDIT: I see in #421 (comment) it's not actually supposed to work this way... In which case I request that a "default() for nulls" or "default() handles nulls" be added, since I cannot figure out a way to do this without removing the null before passing to zod.

(Using 3.0.0-beta.1)

I expect .nullable().default(...) to not have xxx | null in the output type, in the same way that (redundancy aside) .optional().default(...) does not have xxx | undefined, but I'm not 100% sure my reasoning is correct.

Should .default() replace null values, so that number().nullable().default(1) => number or number | null?
Or does .default() only operate on undefineds?

Should the chaining order matter so that number().nullable().default(1) => number and number().default(1).nullable() => number | null?

Example:

// Given
export const PaginationInputSpec = z
  .object({
    page: z.number().default(1),
    perPage: z.number().default(100)
  })
  .nullable()
  .default({});

// I expect:
PaginationInputSpec.parse(undefined) => { page: number, perPage: number }
// AND
PaginationInputSpec.parse(null) => { page: number, perPage: number }

// but I get 
PaginationInputSpec.parse(...) => { page: number, perPage: number } | null

Is this a bug?

I can try to work around it with:

...
  .nullable()
  .transform((i) => i ?? undefined)
  .default({});

which removes the | null from the output type, but it DOESN'T ACTUALLY WORK AT RUNTIME, making the whole thing undefined. I expected the "inner" defaults to then take effect, but they don't.

Using in a GraphQL API where input optionals can be entirely missing (undefined) or explicitly null.

@colinhacks
Copy link
Owner

Like you said there's no way to do this right now. I think I'm going to introduce a "preprocess" method that would let you attach logic like this to your schema prior to parsing, something like this:

const PaginationInputSpec = z
  .object({
    page: z.number().default(1),
    perPage: z.number().default(100),
  })
  .preprocess((val: unknown) => {
    return val ?? {};
  })

But for now the best way is to duplicatively define your defaults:

const PaginationInputSpec = z
    .object({
      page: z.number().default(1),
      perPage: z.number().default(100),
    })
    .nullable()
    .default({})
    .transform((val) => {
      return val ?? { page: 1, perPage: 100 };
    });

@mariusmarais
Copy link
Author

Yes, ended up doing exactly that 😄

@stale
Copy link

stale bot commented Mar 2, 2022

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 wontfix This will not be worked on label Mar 2, 2022
@stale stale bot closed this as completed Mar 9, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
wontfix This will not be worked on
Projects
None yet
Development

No branches or pull requests

2 participants