28

I would like to validate that a field is either a string or an array of strings

Here is a minimal failing example which happens to use formik but actually I am doing server side validation using yup.

  {
    email: yup
      .mixed()
      .oneOf([yup.array().of(yup.string()), yup.string()])
      .nullable()
  }
david_adler
  • 9,690
  • 6
  • 57
  • 97

4 Answers4

28
{
  email: yup.mixed()
    .when('isArray', {
      is: Array.isArray,
      then: yup.array().of(yup.string()),
      otherwise: yup.string(),
    })
}

But a set of checkboxes can produce an array, and text input would not. Are you searching for a solution to validate emails divided by separator?

Tymek
  • 3,000
  • 1
  • 25
  • 46
  • I'm actually doing server side validation of query params - I just forked a formik example as a way of demonstrating the problem – david_adler Jul 03 '19 at 09:47
  • 1
    Sorry but this does not work, as it requires a `isArray` boolean on the object you validate so that we enter the `then` condition. Using `when("email",{})` does not work either, as it produces some kind of infinite loop. – Sebastien Lorber Jun 03 '20 at 15:46
  • 1
    Actually this is better, and works with typescript, just omit the first argument in the when method and it will refer to itself. – giorgiline Nov 12 '21 at 10:01
25

oneOf only works with literal values. Lazy allows you to provide a schema dynamically as shown below

{ 
  email: yup.lazy(val => (Array.isArray(val) ? yup.array().of(yup.string()) : yup.string()))
}
david_adler
  • 9,690
  • 6
  • 57
  • 97
11

This YUP simple validation work for my case when Form contains multi-select field and keeping this field as mandatory and at least one option is required to select.

 selectOptions: array()
         .min(1, "You can't leave this blank.")
         .required("You can't leave this blank.")
         .nullable()
Dharman
  • 30,962
  • 25
  • 85
  • 135
Ajay Kumar
  • 4,864
  • 1
  • 41
  • 44
  • 1
    This doesn't really answer the question though. This is just an array of strings. OP wanted either a string (not in an array) or an array of strings. – acnebs Dec 08 '21 at 06:45
3

David Adler's solution is the working one for me.

Here's a TS-based variant:

  from: Yup.lazy<string | string[]>(from => {
    return Array.isArray(from)
      ? Yup.array()
        .of(PathnameValidator.required())
        .required()
      : PathnameValidator.required();
  }),
Sebastien Lorber
  • 89,644
  • 67
  • 288
  • 419