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

Option for one of the 2 schemas #662

Closed
ankitjaincst opened this issue Oct 17, 2019 · 5 comments
Closed

Option for one of the 2 schemas #662

ankitjaincst opened this issue Oct 17, 2019 · 5 comments

Comments

@ankitjaincst
Copy link

Describe the bug
I have a usecase where both of these are valid to me { colors: "red"} as well as {colors: ["red", "blue"]}

How can I implement something like one of 2 schemas . Eg . Yup.string() || Yup.array().string()

To Reproduce
Provide a running code snippet in using https://repl.it/@jquense/yup or similar

NOTE: if you do not provide a runnable reproduction the chances of getting feedback are significantly lower

Expected behavior
A clear and concise description of what you expected to happen.

Platform (please complete the following information):

  • Browser [e.g. chrome, safari]
  • Version [e.g. 22]

Additional context
Add any other context about the problem here.

@privateOmega
Copy link
Contributor

privateOmega commented Oct 18, 2019

@ankitjaincst This question was raised once in the past. You can find it here #69

Edit:
Added way to use with lazy

const yup = require("yup");

const schema = yup.object().shape({
  colors: yup.lazy(value =>
    typeof value === "string" ? yup.string() : yup.array(yup.string())
  )
});

// Normal Success condition
schema.validateSync({
  colors: "red"
});

schema.validateSync({
  colors: ["red", "blue"]
});

// Normal Failing condition
schema.validateSync({
  colors: 10
});

@privateOmega
Copy link
Contributor

@jquense Is there a better way to do this?

@thecodedrift
Copy link

thecodedrift commented Nov 4, 2019

Update: I just had to do this thrice, so I just made a simple npm module to wrap up testing a(ny) yups: https://www.npmjs.com/package/@aibex/ayup

If you're not concerned about async validation, you can utilize yup.when. This is just a dumb little helper function to connect multiple .when() calls. Because we now have #443 with self references, we can check each condition until we have a validating schema and then tell yup that's the schema we want.

const yup = require("yup");

const ayup = tries => {
  return yup.mixed().when(".", current => {
    for (const t of tries) {
      try {
        t.validateSync(current, {
          strict: true,
        });
        return t;
      } catch (e) {} // discard
    }

    return yup.mixed().test(
      "ayup-failure",
      "No alternatives for ayup were successful",
      v => false
    );
  });
};

const schema = yup.object().shape({
  colors: ayup([yup.string(), yup.array().of(yup.string())])
});

Or alternatively, if you want to write it in a declarative way:

const alternatives = yup.mixed(); // needed for multiple .when
alternatives.when({
  is: v => typeof v === "string",
  then: yup.string()
});
alternatives.when({
  is: v => Array.isArray(v),
  then: yup.array().of(yup.string())
});

const schema = yup.object().shape({
  colors: alternatives
});

Personally, I feel the ayup helper for trying a yup until one succeeds is a bit easier to follow and is closer to the Joi.alternatives() many folks are familiar with.

Edit: This may rely on a bug, as .when is not immutable: https://github.com/jquense/yup/blob/master/src/mixed.js#L429. Each call simply pushes a new condition onto the mixed() object.

@totalolage
Copy link

I was recently faced with this issue, this was my solution:

const schemaA = mixed().test(). --whatever-- .required();
const schemaB = mixed().test(). --whatever-- .required();

const schemaAorB = mixed<InferType<typeof schemaA> | InferType<typeof schemaB>>()
    .test("shape", "invalid", (data) => schemaA.isValidSync(data) || schemaB.isValidSync(data);

@magnuspedro
Copy link

I faced the same issue today, and this was my solution:

const schemaA = yup().object().shape({}). --whatever-- .required();
const schemaB = yup().object().shape({}). --whatever-- .required();

const schemaAorB =  yup.lazy(value => {
    if (value.a) {
      return schemaA
    }
    return schemaB
  })

@jquense jquense closed this as completed Aug 20, 2022
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

6 participants