Repository: jquense/yup
Pull request #2093 (2023-08-23 14:38:28)

Export Lazy and Reference types
Related to #1954 and #1906 ([comment](https://github.com/jquense/yup/issues/1906#issuecomment-1692972416)).


Patch:
diff --git a/README.md b/README.md
index 86c1df7..46a4046 100644
--- a/README.md
+++ b/README.md
@@ -73,6 +73,7 @@ const parsedUser = await userSchema.validate(
 <!-- START doctoc generated TOC please keep comment here to allow auto update -->
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
 
+
 - [Schema basics](#schema-basics)
   - [Parsing: Transforms](#parsing-transforms)
   - [Validation: Tests](#validation-tests)
diff --git a/src/ValidationError.ts b/src/ValidationError.ts
index f02879f..e84c56f 100644
--- a/src/ValidationError.ts
+++ b/src/ValidationError.ts
@@ -5,7 +5,10 @@ let strReg = /\$\{\s*(\w+)\s*\}/g;
 
 type Params = Record<string, unknown>;
 
-export default class ValidationError extends Error {
+export default class ValidationError implements Error {
+  name: string;
+  message: string;
+  stack?: string | undefined;
   value: any;
   path?: string;
   type?: string;
@@ -38,9 +41,8 @@ export default class ValidationError extends Error {
     value?: any,
     field?: string,
     type?: string,
+    disableStack?: boolean,
   ) {
-    super();
-
     this.name = 'ValidationError';
     this.value = value;
     this.path = field;
@@ -52,7 +54,8 @@ export default class ValidationError extends Error {
     toArray(errorOrErrors).forEach((err) => {
       if (ValidationError.isError(err)) {
         this.errors.push(...err.errors);
-        this.inner = this.inner.concat(err.inner.length ? err.inner : err);
+        const innerErrors = err.inner.length ? err.inner : [err];
+        this.inner.push(...innerErrors);
       } else {
         this.errors.push(err);
       }
@@ -63,6 +66,8 @@ export default class ValidationError extends Error {
         ? `${this.errors.length} errors occurred`
         : this.errors[0];
 
-    if (Error.captureStackTrace) Error.captureStackTrace(this, ValidationError);
+    if (!disableStack && Error.captureStackTrace)
+      Error.captureStackTrace(this, ValidationError);
   }
+  [Symbol.toStringTag] = 'Error';
 }
diff --git a/src/index.ts b/src/index.ts
index cd43c5d..00e7930 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -10,8 +10,8 @@ import DateSchema, { create as dateCreate } from './date';
 import ObjectSchema, { AnyObject, create as objectCreate } from './object';
 import ArraySchema, { create as arrayCreate } from './array';
 import TupleSchema, { create as tupleCreate } from './tuple';
-import { create as refCreate } from './Reference';
-import { create as lazyCreate } from './Lazy';
+import Reference, { create as refCreate } from './Reference';
+import Lazy, { create as lazyCreate } from './Lazy';
 import ValidationError from './ValidationError';
 import reach, { getIn } from './util/reach';
 import isSchema from './util/isSchema';
@@ -84,6 +84,8 @@ export type {
   LocaleObject,
   ValidateOptions,
   DefaultThunk,
+  Lazy,
+  Reference,
 };
 
 export {
diff --git a/src/schema.ts b/src/schema.ts
index b8cf53d..b43973d 100644
--- a/src/schema.ts
+++ b/src/schema.ts
@@ -43,6 +43,7 @@ export type SchemaSpec<TDefault> = {
   strip?: boolean;
   strict?: boolean;
   recursive?: boolean;
+  disableStackTrace?: boolean;
   label?: string | undefined;
   meta?: SchemaMetadata;
 };
@@ -191,6 +192,7 @@ export default abstract class Schema<
       strict: false,
       abortEarly: true,
       recursive: true,
+      disableStackTrace: false,
       nullable: false,
       optional: true,
       coerce: true,
@@ -345,6 +347,8 @@ export default abstract class Schema<
       strict: options.strict ?? this.spec.strict,
       abortEarly: options.abortEarly ?? this.spec.abortEarly,
       recursive: options.recursive ?? this.spec.recursive,
+      disableStackTrace:
+        options.disableStackTrace ?? this.spec.disableStackTrace,
     };
   }
 
@@ -499,7 +503,9 @@ export default abstract class Schema<
 
       test(args!, panicOnce, function finishTestRun(err) {
         if (err) {
-          nestedErrors = nestedErrors.concat(err);
+          Array.isArray(err)
+            ? nestedErrors.push(...err)
+            : nestedErrors.push(err);
         }
         if (--count <= 0) {
           nextOnce(nestedErrors);
@@ -553,6 +559,8 @@ export default abstract class Schema<
     options?: ValidateOptions<TContext>,
   ): Promise<this['__outputType']> {
     let schema = this.resolve({ ...options, value });
+    let disableStackTrace =
+      options?.disableStackTrace ?? schema.spec.disableStackTrace;
 
     return new Promise((resolve, reject) =>
       schema._validate(
@@ -563,7 +571,16 @@ export default abstract class Schema<
           reject(error);
         },
         (errors, validated) => {
-          if (errors.length) reject(new ValidationError(errors!, validated));
+          if (errors.length)
+            reject(
+              new ValidationError(
+                errors!,
+                validated,
+                undefined,
+                undefined,
+                disableStackTrace,
+              ),
+            );
           else resolve(validated as this['__outputType']);
         },
       ),
@@ -576,6 +593,8 @@ export default abstract class Schema<
   ): this['__outputType'] {
     let schema = this.resolve({ ...options, value });
     let result: any;
+    let disableStackTrace =
+      options?.disableStackTrace ?? schema.spec.disableStackTrace;
 
     schema._validate(
       value,
@@ -585,7 +604,14 @@ export default abstract class Schema<
         throw error;
       },
       (errors, validated) => {
-        if (errors.length) throw new ValidationError(errors!, value);
+        if (errors.length)
+          throw new ValidationError(
+            errors!,
+            value,
+            undefined,
+            undefined,
+            disableStackTrace,
+          );
         result = validated;
       },
     );
diff --git a/src/types.ts b/src/types.ts
index c63fee5..4a04d59 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -61,6 +61,10 @@ export interface ValidateOptions<TContext = {}> {
    * When false validations will not descend into nested schema (relevant for objects or arrays). Default - true
    */
   recursive?: boolean;
+  /**
+   * When true ValidationError instance won't include stack trace information. Default - false
+   */
+  disableStackTrace?: boolean;
   /**
    * Any context needed for validating schema conditions (see: when())
    */
diff --git a/src/util/createValidation.ts b/src/util/createValidation.ts
index 4d946ba..036fbfd 100644
--- a/src/util/createValidation.ts
+++ b/src/util/createValidation.ts
@@ -22,6 +22,7 @@ export type CreateErrorOptions = {
   message?: Message<any>;
   params?: ExtraParams;
   type?: string;
+  disableStackTrace?: boolean;
 };
 
 export type TestContext<TContext = {}> = {
@@ -79,7 +80,12 @@ export default function createValidation(config: {
     next: NextCallback,
   ) {
     const { name, test, params, message, skipAbsent } = config;
-    let { parent, context, abortEarly = schema.spec.abortEarly } = options;
+    let {
+      parent,
+      context,
+      abortEarly = schema.spec.abortEarly,
+      disableStackTrace = schema.spec.disableStackTrace,
+    } = options;
 
     function resolve<T>(item: T | Reference<T>) {
       return Ref.isRef(item) ? item.getValue(value, parent, context) : item;
@@ -105,6 +111,7 @@ export default function createValidation(config: {
         value,
         nextParams.path,
         overrides.type || name,
+        overrides.disableStackTrace ?? disableStackTrace,
       );
       error.params = nextParams;
       return error;


Test patch:
diff --git a/test/array.ts b/test/array.ts
index d90588f..b01b5b1 100644
--- a/test/array.ts
+++ b/test/array.ts
@@ -1,4 +1,12 @@
-import { string, number, object, array, StringSchema, AnySchema } from '../src';
+import {
+  string,
+  number,
+  object,
+  array,
+  StringSchema,
+  AnySchema,
+  ValidationError,
+} from '../src';
 
 describe('Array types', () => {
   describe('casting', () => {
@@ -56,6 +64,9 @@ describe('Array types', () => {
 
     let merged = array(number()).concat(array(number().required()));
 
+    const ve = new ValidationError('');
+    // expect(ve.toString()).toBe('[object Error]');
+    expect(Object.prototype.toString.call(ve)).toBe('[object Error]');
     expect((merged.innerType as AnySchema).type).toBe('number');
 
     await expect(merged.validateAt('[0]', undefined)).rejects.toThrowError();