hapijs / joi

The most powerful data validation library for JS
Other
20.95k stars 1.51k forks source link

Type definition incorrect for "warning" return type from validateAsync #2927

Closed WVAviator closed 1 year ago

WVAviator commented 1 year ago

Support plan

Context

What are you trying to achieve or the steps to reproduce?

In the type definition for validateAsync, when options include { warnings: true }, the return promise then includes a type of ValidationError[] for the warning. However, the actual value returned appears to be of type ValidationError (or is at least an object with message and details properties), regardless of how many warnings are returned (they are all included in the "details" ValidationErrorItem array, and the error message is a concatenated string of all the warning messages).

To reproduce, run the following code with Node:

import Joi from 'joi';

const warningsIssue = async () => {
  const mySchema = Joi.object({
    a: Joi.any()
      .warning('custom.x', { w: 'world' })
      .messages({ 'custom.x': 'Hello {#w}' }),
    b: Joi.any()
      .warning('custom.y', { w: 'world' })
      .messages({ 'custom.y': 'Hello {#w}' }),
  });

  const myObject = { a: 1, b: '2' };

  try {
    const { warning } = await mySchema.validateAsync(myObject, {
      warnings: true,
    });

    console.log('Warning:', warning);
  } catch (error: any) {
    console.log('Error:', error);
  }
};

warningsIssue();

The logged result will be the following, which is an object where the expected type is an array:

Warning: {
  message: 'Hello world',
  details: [
    {
      message: 'Hello world',
      path: [Array],
      type: 'custom.x',
      context: [Object]
    },
    {
      message: 'Hello world',
      path: [Array],
      type: 'custom.y',
      context: [Object]
    }
  ]
}

Here is the type definition for validateAsync:

        /**
         * Validates a value using the schema and options.
         */
        validateAsync<TOpts extends AsyncValidationOptions>(
          value: any,
          options?: TOpts
        ): Promise<
          TOpts extends { artifacts: true } | { warnings: true }
            ? { value: TSchema } & (TOpts extends { artifacts: true }
            ? { artifacts: Map<any, string[][]> }
            : {}) &
            (TOpts extends { warnings: true }
              ? { warning: ValidationError[] } // actual return is ValidationError
              : {})
            : TSchema
          >;

What was the result you got?

I got a type error when trying to access and iterate over the details property of the resolved value for warning from validateAsync({ warnings: true }).

What result did you expect?

No type errors and a type definition that aligns with the return value

WVAviator commented 1 year ago

If you need someone to contribute to this, go ahead and assign me and I'll get a PR going for it. Thanks!

Marsup commented 1 year ago

Thanks for the report and for offering your help, I felt it would be easier if I made the fix. It's not exactly a ValidationError as it doesn't inherit from Error, but it shares most of its properties indeed.