Skip to content
This repository has been archived by the owner on Aug 26, 2022. It is now read-only.

Latest commit

 

History

History
103 lines (74 loc) · 3.19 KB

extending.md

File metadata and controls

103 lines (74 loc) · 3.19 KB

Extending Schema

For simple cases where you want to reuse common schema configurations, creating and passing around instances works great and is automatically typed correctly

import * as yup from 'yup';

const requiredString = yup.string().required().default('');

const momentDate = (parseFormats = ['MMM dd, yyy']) =>
  yup.date().transform(function (value, originalValue) {
    if (this.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 };

Schema are immutable so each can be configured further without changing the original.

Extending Schema with new methods

yup provides a addMethod() utility for extending built-in schema:

function parseDateFromFormats(formats, parseStrict) {
  return this.transform(function (value, originalValue) {
    if (this.isType(value)) return value;

    value = Moment(originalValue, formats, parseStrict);

    return value.isValid() ? value.toDate() : yup.date.INVALID_DATE;
  });
}

yup.addMethod(yup.date, 'format', parseDateFromFormats);

Note that addMethod isn't magic, it mutates the prototype of the passed in schema.

Note: if you are using TypeScript you also need to adjust the class or interface see the typescript docs for details.

Creating new Schema types

If you're use case calls for creating an entirely new type. inheriting from and existing schema class may be best: Generally you should not inheriting from the abstract BaseSchema unless you know what you are doing. The other types are fair game though.

You should keep in mind some basic guidelines when extending schemas:

  • never mutate an existing schema, always clone() and then mutate the new one before returning it. Built-in methods like test and transform take care of this for you, so you can safely use them (see below) without worrying

  • transforms should never mutate the value passed in, and should return an invalid object when one exists (NaN, InvalidDate, etc) instead of null for bad values.

  • by the time validations run the value is guaranteed to be the correct type, however it still may 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);
  }
}

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)