jquense / yup

Dead simple Object schema validation
MIT License
22.94k stars 935 forks source link

Method added with 'addMethod' to yup.array type is not working when used inside 'when' with 'then' clause #2254

Closed akan01n closed 3 weeks ago

akan01n commented 3 weeks ago

Describe the bug I created a new method using the 'addMethod' function, but when I try to use this new method with 'when' inside the 'then' clause, the function is not called. But if I use this new function without 'when' clause, it works.

To Reproduce

Just simple code with example to show the problem.

declare module 'yup' {
  interface ArraySchema<
    TIn extends any[] | null | undefined,
    TContext,
    TDefault = undefined,
    TFlags extends yup.Flags = '',
  > extends yup.Schema<TIn, TContext, TDefault, TFlags> {
    isArraySorted(): ArraySchema<TIn, TContext, TDefault, TFlags>;
  }
}

addMethod(array, "isArraySorted", function isArraySorted() {
  console.log(">> isArraySorted"); // <---- when validation is running this log is printed
  return this.test("isArraySorted", "-", function (values) {
    if (!Array.isArray(values)) {
      return this.createError({
        path: this.path,
        message: "Type is not an array",
      });
    }
    for (let i = 0; i < values.length; i += 1) {
      if (values?.[i + 1] && values[i] >= values?.[i + 1]) {
        return this.createError({
          path: this.path,
          message: "Array not sorted",
        });
      }
    }
    return true;
  });
});

Working Schema 'isArraySorted' is called

const optsFormSchema = yup.object({
  optsA: yup.boolean().required(),
  optsB: yup.object({
    list: yup.array().of(yup.number()).isArraySorted(),
  }),
});

NOT working Schema 'isArraySorted' not called

const optsFormSchema = yup.object({
  optsA: yup.boolean().required(),
  optsB: yup.object({
    list: yup
      .array()
      .of(yup.number())
      .when([
        "optsA",
        {
          is: true,
          then: (schema) => schema.isArraySorted(),
        },
      ]),
  }),
});

Expected behavior The custom function created with 'addMethod' to work inside 'when' clause.

Platform (please complete the following information):

Additional context The point here is to check if the custom method added with 'addMethod' is being called inside the 'then' clause.

Thanks :)

akan01n commented 3 weeks ago

After some tests. I added a 'otherwise' clause and the function is called inside the otherwise. So "optsA" is always "false".

Is it because optsB is nested and "when" can't see the parent optsA ??

akan01n commented 3 weeks ago

The solution I found so far is to move optsA inside optsB.

const optsFormSchema = yup.object({
  optsB: yup.object({
    optsA: yup.boolean().required(),
    list: yup
      .array()
      .of(yup.number())
      .when([
        "optsA",
        {
          is: true,
          then: (schema) => schema.isArraySorted(),
        },
      ]),
  }),
});

I found others solutions, but in my case I am using react-hooks-form and I can't reference 'context' to add to Yup validator.