Yup

repository·master·Indexed 12 days ago

https://github.com/jquense/yup

A schema builder for runtime value parsing and validation. Yup allows developers to define complex data models, transform values via casting, and assert validity with built-in async support and powerful TypeScript integration using InferType. Version 1.7.1.

Tokens
21.6K
Snippets
83
Records
104
Agent score
97%

What's inside Yup

  1. Handle default values in object schemas

    master

    Object schemas can provide default values for their fields using .default(). Calling .default() on the schema itself builds out the object shape and fills in all defaults for the entire nested structure.

    Warning on Nested Objects: If a nested object is optional but contains non-optional fields, validation might fail unexpectedly because Yup casts the input before validating. To avoid this, either set the nested default to undefined or mark it as nullable() and default to null.

    let schema = object({
      name: string().default(''),
    });
    
    schema.default(); // -> { name: '' }
    
    // To avoid unexpected validation failures in nested objects:
    let safeSchema = object({
      id: string().required(),
      names: object({
        first: string().required(),
      }).default(undefined), // Option 1: Set default to undefined
      // OR
      // names: object({ first: string().required() }).nullable().default(null), // Option 2
    });
  2. Create new Schema types by inheriting from existing classes

    master

    To create an entirely new schema type, inherit from an existing schema class (e.g., DateSchema, StringSchema) rather than the abstract Schema class.

    Guidelines for extending schemas:

    • Immutability: Never mutate an existing schema instance. Always use .clone() to create a new instance before applying mutations. Built-in methods like .test() and .transform() handle cloning automatically.
    • Transforms: Transforms should never mutate the input value. If a value is invalid, return an invalid object (like NaN or InvalidDate) instead of null.
    • Type Safety: By the time validations run, the value is guaranteed to be the correct type, though it may still be null or undefined.
    import { DateSchema } from 'yup';
    
    class MomentDateSchema extends DateSchema {
      static create() {
        return MomentDateSchema();
      }
    
      constructor() {
        super();
        this._validFormats = [];
    
        this.withMutation(() => {
          this.transform(function (value, originalValue) {
            if (this.isType(value))
              // we have a valid value
              return value;
            return Moment(originalValue, this._validFormats, true);
          });
        });
      }
    
      _typeCheck(value) {
        return (
          super._typeCheck(value) || (moment.isMoment(value) && value.isValid())
        );
      }
    
      format(formats) {
        if (!formats) throw new Error('must enter a valid format');
        let next = this.clone();
        next._validFormats = {}.concat(formats);
        return next;
      }
    }
    
    let schema = new MomentDateSchema();
    
    schema.format('YYYY-MM-DD').cast('It is 2012-05-25'); // => Fri May 25 2012 00:00:00 GMT-0400 (Eastern Daylight Time)
  3. Reuse schema configurations by creating instances

    master

    For simple reuse of common schema configurations, you can create and export schema instances. Because Yup schemas are immutable, you can further configure these exported instances in other parts of your application without affecting the original definition.

    import * as yup from 'yup';
    
    const requiredString = yup.string().required().default('');
    
    const momentDate = (parseFormats = ['MMM dd, yyy']) =>
      yup.date().transform((value, originalValue, schema) => {
        if (schema.isType(value)) return value;
    
        // the default coercion transform failed so let's try it with Moment instead
        value = Moment(originalValue, parseFormats);
        return value.isValid() ? value.toDate() : yup.date.INVALID_DATE;
      });
    
    export { momentDate, requiredString };
  4. Get started with Yup

    master

    Yup is a schema builder used for runtime value parsing and validation. You can define a schema to transform values to match a specific shape, assert the validity of existing values, or both.

    Schemas are built by chaining methods together. They consist of two main components:

    1. Parsing actions (transforms): Coercing or casting values into the correct type.
    2. Assertions (tests): Validating that the value meets specific criteria.

    Key features include:

    • Powerful TypeScript support via InferType to derive static types from schemas.
    • Built-in async validation support.
    • Extensibility for custom type-safe methods.
    • Compatibility with Standard Schema.
    import { object, string, number, date, InferType } from 'yup';
    
    let userSchema = object({
      name: string().required(),
      age: number().required().positive().integer(),
      email: string().email(),
      website: string().url().nullable(),
      createdOn: date().default(() => new Date()),
    });
    
    // parse and assert validity
    let user = await userSchema.validate(await fetchUser());
    
    type User = InferType<typeof userSchema>;
  5. How Reference scopes work

    master

    The Reference class uses prefixes to resolve values from different scopes during validation. The scope is determined by the first character of the key string:

    PrefixScopeDescription
    $ContextAccesses data passed in the context object during validation.
    .ValueAccesses properties of the current value being validated.
    (none)SiblingAccesses properties of the parent object (the sibling fields).

    When a reference is resolved, it uses a getter (via property-expr) to traverse the path and an optional map function to transform the result.

  6. Configure Schema behavior with SchemaSpec

    master

    A Yup schema's behavior is governed by its spec. You can influence these settings when cloning or creating schemas. Key configuration options include:

    • coerce: Whether to attempt to cast values to the target type (defaults to true).
    • nullable: Whether the schema allows null values.
    • optional: Whether the schema allows undefined values.
    • default: A value or a function that returns a value to use if the input is undefined.
    • abortEarly: If true, validation stops at the first error found (defaults to true).
    • strip: If true, unknown keys are removed from the object during casting.
    • strict: If true, no type coercion is performed.
    • recursive: Whether to validate nested objects/arrays.
    • label: A string used for error messages to identify the field.
    • meta: An object for storing custom metadata.
  7. Understand Standard Schema compatibility in Yup

    master

    Yup implements the Standard Schema specification, allowing it to interoperate with other tools that support this common interface. A StandardSchema object provides a ~standard property containing metadata and a validate method.

    StandardSchema Interface

    • version: Must be 1.
    • vendor: A string identifying the implementation (e.g., yup).
    • validate(value: unknown): An async or sync function that returns a StandardResult.
    • types?: Optional metadata describing the input and output types.

    StandardResult Types

    • StandardSuccessResult: Contains the validated value.
    • StandardFailureResult: Contains an array of issues.

    StandardIssue Structure

    Each issue includes:

    • message: A string describing the error.
    • path?: An array of PropertyKey or StandardPathSegment indicating where the error occurred.
  8. Customize error messages with LocaleObject

    master

    Yup allows you to customize error messages for different validation types using a LocaleObject. This object is composed of several sub-locales categorized by data type: mixed, string, number, date, boolean, object, array, and tuple.

    Each message can be a string template using placeholders like ${path}, ${min}, ${max}, ${values}, etc., or a function that receives validation parameters to generate a dynamic message.

    To customize messages, you can provide a new LocaleObject to the Yup configuration (typically via setLocale).

    import { setLocale } from 'yup';
    
    setLocale({
      string: {
        min: ({ min, path }) => `${path} is too short (minimum ${min} required)`
      },
      number: {
        integer: 'This must be a whole number'
      }
    });
  9. Customize error messages with the Message type

    master

    Yup allows for flexible error message definitions. A message can be a simple string, a Record<PropertyKey, unknown> (object), or a function that receives detailed parameters to generate a dynamic message.

    When using a function, it receives MessageParams which includes:

    • path: The path to the field.
    • value: The current value.
    • originalValue: The value before transformations.
    • originalPath: The original path.
    • label: The field label.
    • type: The type of validation that failed.
    • spec: The schema specification for the test.
    // Example of a dynamic message function
    schema.test('is-even', (value, context) => {
      return value % 2 === 0;
    }, (params) => ({
      message: `${params.label} must be an even number. Received: ${params.value}`
    }));
  10. Use tests to validate data

    master

    Tests assert that inputs meet specific criteria without altering the data. You can use built-in tests like .min() or .email(), or create custom tests using .test().

    When a test fails, Yup throws a ValidationError containing metadata like the test name, arguments, and the field path.

    string()
      .min(3, 'must be at least 3 characters long')
      .email('must be a valid email')
      .validate('no'); // Throws ValidationError
    
    let jamesSchema = string().test(
      'is-james',
      (d) => `${d.path} is not James`,
      (value) => value == null || value === 'James',
    );
    
    jamesSchema.validateSync('James'); // "James"
    jamesSchema.validateSync('Jane'); // ValidationError "this is not James"
  11. Extend built-in schemas with addMethod

    master

    You can extend Yup's built-in schema types using TypeScript's interface merging and the addMethod function. This is useful for adding custom domain-specific validation or transformation logic.

    1. Declare the type extension in an ambient type definition file (e.g., globals.d.ts).
    2. Implement the method in your application code using addMethod.
    // globals.d.ts
    declare module 'yup' {
      interface StringSchema<TType, TContext, TDefault, TFlags> {
        append(appendStr: string): this;
      }
    }
    
    // app.ts
    import { addMethod, string } from 'yup';
    
    addMethod(string, 'append', function append(appendStr: string) {
      return this.transform((value) => `${value}${appendStr}`);
    });
    
    string().append('~~~~').cast('hi'); // 'hi~~~~'
  12. Implement i18n with setLocale functions

    master

    For multi-language support, setLocale can accept functions that return objects containing translation keys and values. These objects can then be passed to an i18n library like i18next.

    import { setLocale } from 'yup';
    
    setLocale({
      mixed: {
        default: 'field_invalid',
      },
      number: {
        min: ({ min }) => ({ key: 'field_too_short', values: { min } }),
        max: ({ max }) => ({ key: 'field_too_big', values: { max } }),
      },
    });
    
    let schema = yup.object().shape({
      name: yup.string(),
    });
    
    try {
      await schema.validate({ name: 'jimmy' });
    } catch (err) {
      // Map error keys to your i18n library
      const messages = err.errors.map((err) => i18next.t(err.key, err.values));
    }
    import { setLocale } from 'yup';
    
    setLocale({
      // use constant translation keys for messages without values
      mixed: {
        default: 'field_invalid',
      },
      // use functions to generate an error object that includes the value from the schema
      number: {
        min: ({ min }) => ({ key: 'field_too_short', values: { min } }),
        max: ({ max }) => ({ key: 'field_too_big', values: { max } }),
      },
    });
    
    // ...
    let schema = yup.object().shape({
      name: yup.string(),
    });
    
    try {
      await schema.validate({ name: 'jimmy', age: 11 });
    } catch (err) {
      messages = err.errors.map((err) => i18next.t(err.key));
    }