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

Add support for non optional custom zod types #350

Merged
merged 7 commits into from
Oct 30, 2024

Conversation

pmsfc
Copy link
Contributor

@pmsfc pmsfc commented Oct 28, 2024

No description provided.

@samchungy
Copy link
Owner

This looks okay to me, I'll have another look when I have time to properly suss it out.

But would z.coerce.date() be more appropriate in your scenario?

@pmsfc
Copy link
Contributor Author

pmsfc commented Oct 28, 2024

This looks okay to me, I'll have another look when I have time to properly suss it out.

But would z.coerce.date() be more appropriate in your scenario?

I'm using LocalDate from the js-joda lib, just used Date as an example here.

@samchungy
Copy link
Owner

Hmmm I'm not entirely sure I like the approach of treating a custom as non optional by default given it can potentially return anything. Including undefined which in JavaScript world is also optional

@pmsfc
Copy link
Contributor Author

pmsfc commented Oct 29, 2024

Hmmm I'm not entirely sure I like the approach of treating a custom as non optional by default given it can potentially return anything. Including undefined which in JavaScript world is also optional

This solution won't do that because it's still calling zod's isOptional(), for example:

it('returns true for a custom undefined without a check', () => {
  const schema = z.custom<undefined>();
  const result = isOptionalSchema(schema, createOutputState());

  expect(result).toEqual({ optional: true });
});

it('returns true for a custom undefined with a check', () => {
  const schema = z.custom<undefined>((_: unknown) => false);
  const result = isOptionalSchema(schema, createOutputState());

  expect(result).toEqual({ optional: false });
});

For ex both of these will pass because zod will call the the safeParse(undefined).success when calling isOptional()

@samchungy
Copy link
Owner

You could also return something | undefined though in a custom.

@pmsfc
Copy link
Contributor Author

pmsfc commented Oct 29, 2024

You could also return something | undefined though in a custom.

From my understanding (I may be wrong) the type you pass to the custom won't matter. If you don't provide a check the schema.isOptional() will return true for custom types. But if you provide a check it won't be, its up to the check function to validate if it's optional by accepting undefined as a parsable input.

This is mainly because a z.custom without a check is a zod type of ZodAny but if it contains a check its a ZodEffects

For custom of string | undefined the following tests pass

  it('returns false for a custom string | undefined without a check', () => {
    const schema = z.custom<string | undefined>();
    const result = isOptionalSchema(schema, createOutputState());

    expect(result).toEqual({ optional: true });
  });

  it('returns false for a custom string | undefined with a check that returns false for undefined', () => {
    const schema = z.custom<string | undefined>(
      (toCheck: string | undefined) => toCheck !== undefined,
    );
    const result = isOptionalSchema(schema, createOutputState());

    expect(result).toEqual({ optional: false });
  });

  it('returns true for a custom string | undefined with a check that returns true for undefined', () => {
    const schema = z.custom<string | undefined>(
      (toCheck: string | undefined) => toCheck === undefined,
    );
    const result = isOptionalSchema(schema, createOutputState());

    expect(result).toEqual({ optional: true });
  });

@samchungy
Copy link
Owner

@samchungy
Copy link
Owner

Thank you for your work looking into this! I will take a final look tomorrow 🙂

@samchungy
Copy link
Owner

Thanks for this, I might end up re-writing all of the optional checking logic though but I'll keep your test cases

@samchungy samchungy merged commit c60ae06 into samchungy:master Oct 30, 2024
1 check passed
@github-actions github-actions bot mentioned this pull request Oct 30, 2024
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

Successfully merging this pull request may close these issues.

2 participants