Dead simple Object schema validation

Related tags

Validation yup
Overview

Yup

Yup is a JavaScript schema builder for value parsing and validation. Define a schema, transform a value to match, validate the shape of an existing value, or both. Yup schema are extremely expressive and allow modeling complex, interdependent validations, or value transformations.

Yup's API is heavily inspired by Joi, but leaner and built with client-side validation as its primary use-case. Yup separates the parsing and validating functions into separate steps. cast() transforms data while validate checks that the input is the correct shape. Each can be performed together (such as HTML form validation) or seperately (such as deserializing trusted data from APIs).

Docs

Install

npm install -S yup

Yup always relies on the Promise global object to handle asynchronous values as well as Set and Map. For browsers that do not support these, you'll need to include a polyfill, such as core-js:

import 'core-js/es6/promise';
import 'core-js/es6/set';
import 'core-js/es6/map';

Usage

You define and create schema objects. Schema objects are immutable, so each call of a method returns a new schema object. When using es module syntax, yup exports everything as a named export

import * as yup from 'yup';

let schema = yup.object().shape({
  name: yup.string().required(),
  age: yup.number().required().positive().integer(),
  email: yup.string().email(),
  website: yup.string().url(),
  createdOn: yup.date().default(function () {
    return new Date();
  }),
});

// check validity
schema
  .isValid({
    name: 'jimmy',
    age: 24,
  })
  .then(function (valid) {
    valid; // => true
  });

// you can try and type cast objects to the defined schema
schema.cast({
  name: 'jimmy',
  age: '24',
  createdOn: '2014-09-23T19:25:25Z',
});
// => { name: 'jimmy', age: 24, createdOn: Date }

The exported functions are factory methods for constructing schema instances, but without the new keyword. If you need access to the actual schema classes, they are also exported:

import {
  BooleanSchema,
  DateSchema,
  MixedSchema,
  NumberSchema,
  ArraySchema,
  ObjectSchema,
  StringSchema,
} from 'yup';

If you're looking for an easily serializable DSL for yup schema, check out yup-ast

Using a custom locale dictionary

Allows you to customize the default messages used by Yup, when no message is provided with a validation test. If any message is missing in the custom dictionary the error message will default to Yup's one.

import { setLocale } from 'yup';

setLocale({
  mixed: {
    default: 'Não é válido',
  },
  number: {
    min: 'Deve ser maior que ${min}',
  },
});

// now use Yup schemas AFTER you defined your custom dictionary
let schema = yup.object().shape({
  name: yup.string(),
  age: yup.number().min(18),
});

schema.validate({ name: 'jimmy', age: 11 }).catch(function (err) {
  err.name; // => 'ValidationError'
  err.errors; // => ['Deve ser maior que 18']
});

If you need multi-language support, Yup has got you covered. The function setLocale accepts functions that can be used to generate error objects with translation keys and values. Just get this output and feed it into your favorite i18n library.

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 } }),
  },
});

// now use Yup schemas AFTER you defined your custom dictionary
let schema = yup.object().shape({
  name: yup.string(),
  age: yup.number().min(18),
});

schema.validate({ name: 'jimmy', age: 11 }).catch(function (err) {
  err.name; // => 'ValidationError'
  err.errors; // => [{ key: 'field_too_short', values: { min: 18 } }]
});

API

yup

The module export.

let yup = require('yup');

yup.mixed;
yup.string;
yup.number;
yup.boolean; // also aliased as yup.bool
yup.date;
yup.object;
yup.array;

yup.reach;
yup.addMethod;
yup.ref;
yup.lazy;
yup.setLocale;
yup.ValidationError;

yup.reach(schema: Schema, path: string, value?: object, context?: object): Schema

For nested schemas yup.reach will retrieve a nested schema based on the provided path.

For nested schemas that need to resolve dynamically, you can provide a value and optionally a context object.

let schema = object().shape({
  nested: object().shape({
    arr: array().of(object().shape({ num: number().max(4) })),
  }),
});

reach(schema, 'nested.arr.num');
reach(schema, 'nested.arr[].num');
reach(schema, 'nested.arr[1].num');
reach(schema, 'nested["arr"][1].num');

yup.addMethod(schemaType: Schema, name: string, method: ()=> Schema): void

Adds a new method to the core schema types. A friendlier convenience method for schemaType.prototype[name] = method.

yup.addMethod(yup.date, 'format', function (formats, parseStrict) {
  return this.transform(function (value, originalValue) {
    if (this.isType(value)) return value;

    value = Moment(originalValue, formats, parseStrict);

    return value.isValid() ? value.toDate() : new Date('');
  });
});

yup.ref(path: string, options: { contextPrefix: string }): Ref

Creates a reference to another sibling or sibling descendant field. Refs are resolved at validation/cast time and supported where specified. Refs are evaluated in the proper order so that the ref value is resolved before the field using the ref (be careful of circular dependencies!).

let schema = object({
  baz: ref('foo.bar'),
  foo: object({
    bar: string(),
  }),
  x: ref('$x'),
});

schema.cast({ foo: { bar: 'boom' } }, { context: { x: 5 } });
// => { baz: 'boom',  x: 5, foo: { bar: 'boom' } }

yup.lazy((value: any) => Schema): Lazy

Creates a schema that is evaluated at validation/cast time. Useful for creating recursive schema like Trees, for polymorphic fields and arrays.

CAUTION! When defining parent-child recursive object schema, you want to reset the default() to undefined on the child—otherwise the object will infinitely nest itself when you cast it!

let node = object({
  id: number(),
  child: yup.lazy(() => node.default(undefined)),
});

let renderable = yup.lazy((value) => {
  switch (typeof value) {
    case 'number':
      return number();
    case 'string':
      return string();
    default:
      return mixed();
  }
});

let renderables = array().of(renderable);

ValidationError(errors: string | Array<string>, value: any, path: string)

Thrown on failed validations, with the following properties

  • name: "ValidationError"
  • path: a string, indicating where there error was thrown. path is empty at the root level.
  • errors: array of error messages
  • inner: in the case of aggregate errors, inner is an array of ValidationErrors throw earlier in the validation chain. When the abortEarly option is false this is where you can inspect each error thrown, alternatively, errors will have all of the messages from each inner error.

mixed

Creates a schema that matches all types. All types inherit from this base type

let schema = yup.mixed();

schema.isValid(undefined, function (valid) {
  valid; // => true
});

mixed.clone(): Schema

Creates a deep copy of the schema. Clone is used internally to return a new schema with every schema state change.

mixed.label(label: string): Schema

Overrides the key name which is used in error messages.

mixed.meta(metadata: object): Schema

Adds to a metadata object, useful for storing data with a schema, that doesn't belong the cast object itself.

mixed.describe(): SchemaDescription

Collects schema details (like meta, labels, and active tests) into a serializable description object.

SchemaDescription {
  type: string,
  label: string,
  meta: object,
  tests: Array<{ name: string, params: object }>
}

mixed.concat(schema: Schema): Schema

Creates a new instance of the schema by combining two schemas. Only schemas of the same type can be concatenated.

mixed.validate(value: any, options?: object): Promise<any, ValidationError>

Returns the value (a cast value if isStrict is false) if the value is valid, and returns the errors otherwise. This method is asynchronous and returns a Promise object, that is fulfilled with the value, or rejected with a ValidationError.

The options argument is an object hash containing any schema options you may want to override (or specify for the first time).

Options = {
  strict: boolean = false;
  abortEarly: boolean = true;
  stripUnknown: boolean = false;
  recursive: boolean = true;
  context?: object;
}
  • strict: only validate the input, and skip any coercion or transformation
  • abortEarly: return from validation methods on the first error rather than after all validations run.
  • stripUnknown: remove unspecified keys from objects.
  • recursive: when false validations will not descend into nested schema (relevant for objects or arrays).
  • context: any context needed for validating schema conditions (see: when())
schema.validate({ name: 'jimmy', age: 24 }).then(function (value) {
  value; // => { name: 'jimmy',age: 24 }
});

schema.validate({ name: 'jimmy', age: 'hi' }).catch(function (err) {
  err.name; // => 'ValidationError'
  err.errors; // => ['age must be a number']
});

mixed.validateSync(value: any, options?: object): any

Runs validatations synchronously if possible and returns the resulting value, or throws a ValidationError. Accepts all the same options as validate.

Synchronous validation only works if there are no configured async tests, e.g tests that return a Promise. For instance this will work:

let schema = number().test(
  'is-42',
  "this isn't the number i want",
  (value) => value != 42,
);

schema.validateSync(23); // throws ValidationError

however this will not:

let schema = number().test('is-42', "this isn't the number i want", (value) =>
  Promise.resolve(value != 42),
);

schema.validateSync(42); // throws Error

mixed.validateAt(path: string, value: any, options?: object): Promise<any, ValidationError>

Validate a deeply nested path within the schema. Similar to how reach works, but uses the resulting schema as the subject for validation.

Note! The value here is the root value relative to the starting schema, not the value at the nested path.

let schema = object({
  foo: array().of(
    object({
      loose: boolean(),
      bar: string().when('loose', {
        is: true,
        otherwise: (s) => s.strict(),
      }),
    }),
  ),
});

let rootValue = {
  foo: [{ bar: 1 }, { bar: 1, loose: true }],
};

await schema.validateAt('foo[0].bar', rootValue); // => ValidationError: must be a string

await schema.validateAt('foo[1].bar', rootValue); // => '1'

mixed.validateSyncAt(path: string, value: any, options?: object): any

Same as validateAt but synchronous.

mixed.isValid(value: any, options?: object): Promise<boolean>

Returns true when the passed in value matches the schema. isValid is asynchronous and returns a Promise object.

Takes the same options as validate().

mixed.isValidSync(value: any, options?: object): boolean

Synchronously returns true when the passed in value matches the schema.

Takes the same options as validateSync() and has the same caveats around async tests.

mixed.cast(value: any, options = {}): any

Attempts to coerce the passed in value to a value that matches the schema. For example: '5' will cast to 5 when using the number() type. Failed casts generally return null, but may also return results like NaN and unexpected strings.

options parameter can be an object containing context. (For more info on context see mixed.validate)

mixed.isType(value: any): boolean

Runs a type check against the passed in value. It returns true if it matches, it does not cast the value. When nullable() is set null is considered a valid value of the type. You should use isType for all Schema type checks.

mixed.strict(isStrict: boolean = false): Schema

Sets the strict option to true. Strict schemas skip coercion and transformation attempts, validating the value "as is".

mixed.strip(stripField: boolean = true): Schema

Marks a schema to be removed from an output object. Only works as a nested schema.

let schema = object({
  useThis: number(),
  notThis: string().strip(),
});

schema.cast({ notThis: 'foo', useThis: 4 }); // => { useThis: 4 }

mixed.withMutation(builder: (current: Schema) => void): void

First the legally required Rich Hickey quote:

If a tree falls in the woods, does it make a sound?

If a pure function mutates some local data in order to produce an immutable return value, is that ok?

withMutation allows you to mutate the schema in place, instead of the default behavior which clones before each change. Generally this isn't necessary since the vast majority of schema changes happen during the initial declaration, and only happen once over the lifetime of the schema, so performance isn't an issue. However certain mutations do occur at cast/validation time, (such as conditional schema using when()), or when instantiating a schema object.

object()
  .shape({ key: string() })
  .withMutation((schema) => {
    return arrayOfObjectTests.forEach((test) => {
      schema.test(test);
    });
  });

mixed.default(value: any): Schema

Sets a default value to use when the value is undefined. Defaults are created after transformations are executed, but before validations, to help ensure that safe defaults are specified. The default value will be cloned on each use, which can incur performance penalty for objects and arrays. To avoid this overhead you can also pass a function that returns a new default. Note that null is considered a separate non-empty value.

yup.string.default('nothing');

yup.object.default({ number: 5 }); // object will be cloned every time a default is needed

yup.object.default(() => ({ number: 5 })); // this is cheaper

yup.date.default(() => new Date()); // also helpful for defaults that change over time

mixed.getDefault(options?: object): Any

Retrieve a previously set default value. getDefault will resolve any conditions that may alter the default. Optionally pass options with context (for more info on context see mixed.validate).

mixed.nullable(isNullable: boolean = true): Schema

Indicates that null is a valid value for the schema. Without nullable() null is treated as a different type and will fail isType() checks.

mixed.required(message?: string | function): Schema

Mark the schema as required, which will not allow undefined or null as a value. Note that unless a schema is marked as nullable() a null value is treated as a type error, not a missing value. Mark a schema as mixed().nullable().required() treat null as missing.

Watch out! string().required) works a little different and additionally prevents empty string values ('') when required.

mixed.notRequired(): Schema Alias: optional()

Mark the schema as not required. Passing undefined (or null for nullable schema) as value will not fail validation.

mixed.defined(): Schema

Require a value for the schema. All field values apart from undefined meet this requirement.

mixed.typeError(message: string): Schema

Define an error message for failed type checks. The ${value} and ${type} interpolation can be used in the message argument.

mixed.oneOf(arrayOfValues: Array<any>, message?: string | function): Schema Alias: equals

Whitelist a set of values. Values added are automatically removed from any blacklist if they are in it. The ${values} interpolation can be used in the message argument.

Note that undefined does not fail this validator, even when undefined is not included in arrayOfValues. If you don't want undefined to be a valid value, you can use mixed.required.

let schema = yup.mixed().oneOf(['jimmy', 42]);

await schema.isValid(42); // => true
await schema.isValid('jimmy'); // => true
await schema.isValid(new Date()); // => false

mixed.notOneOf(arrayOfValues: Array<any>, message?: string | function)

Blacklist a set of values. Values added are automatically removed from any whitelist if they are in it. The ${values} interpolation can be used in the message argument.

let schema = yup.mixed().notOneOf(['jimmy', 42]);

await schema.isValid(42); // => false
await schema.isValid(new Date()); // => true

mixed.when(keys: string | Array<string>, builder: object | (value, schema)=> Schema): Schema

Adjust the schema based on a sibling or sibling children fields. You can provide an object literal where the key is is value or a matcher function, then provides the true schema and/or otherwise for the failure condition.

is conditions are strictly compared (===) if you want to use a different form of equality you can provide a function like: is: (value) => value == true.

Like joi you can also prefix properties with $ to specify a property that is dependent on context passed in by validate() or isValid. when conditions are additive.

let schema = object({
  isBig: boolean(),
  count: number()
    .when('isBig', {
      is: true, // alternatively: (val) => val == true
      then: yup.number().min(5),
      otherwise: yup.number().min(0),
    })
    .when('$other', (other, schema) => (other === 4 ? schema.max(6) : schema)),
});

await schema.validate(value, { context: { other: 4 } });

You can also specify more than one dependent key, in which case each value will be spread as an argument.

let schema = object({
  isSpecial: boolean(),
  isBig: boolean(),
  count: number().when(['isBig', 'isSpecial'], {
    is: true, // alternatively: (isBig, isSpecial) => isBig && isSpecial
    then: yup.number().min(5),
    otherwise: yup.number().min(0),
  }),
});

await schema.validate({
  isBig: true,
  isSpecial: true,
  count: 10,
});

Alternatively you can provide a function that returns a schema (called with the value of the key and the current schema).

let schema = yup.object({
  isBig: yup.boolean(),
  count: yup.number().when('isBig', (isBig, schema) => {
    return isBig ? schema.min(5) : schema.min(0);
  }),
});

await schema.validate({ isBig: false, count: 4 });

mixed.test(name: string, message: string | function, test: function): Schema

Adds a test function to the validation chain. Tests are run after any object is cast. Many types have some tests built in, but you can create custom ones easily. In order to allow asynchronous custom validations all (or no) tests are run asynchronously. A consequence of this is that test execution order cannot be guaranteed.

All tests must provide a name, an error message and a validation function that must return true when the current value is valid and false or a ValidationError otherwise. To make a test async return a promise that resolves true or false or a ValidationError.

For the message argument you can provide a string which will interpolate certain values if specified using the ${param} syntax. By default all test messages are passed a path value which is valuable in nested schemas.

The test function is called with the current value. For more advanced validations you can use the alternate signature to provide more options (see below):

let jimmySchema = string().test(
  'is-jimmy',
  '${path} is not Jimmy',
  (value, context) => value === 'jimmy',
);

// or make it async by returning a promise
let asyncJimmySchema = string().test(
  'is-jimmy',
  '${path} is not Jimmy',
  async (value, testContext) => (await fetch('/is-jimmy/' + value)).responseText === 'true',
});

await schema.isValid('jimmy'); // => true
await schema.isValid('john'); // => false

Test functions are called with a special context value, as the second argument, that exposes some useful metadata and functions. For non arrow functions, the test context is also set as the function this. Watch out, if you access it via this it won't work in an arrow function.

  • testContext.path: the string path of the current validation
  • testContext.schema: the resolved schema object that the test is running against.
  • testContext.options: the options object that validate() or isValid() was called with
  • testContext.parent: in the case of nested schema, this is the value of the parent object
  • testContext.originalValue: the original value that is being tested
  • testContext.createError(Object: { path: String, message: String, params: Object }): create and return a validation error. Useful for dynamically setting the path, params, or more likely, the error message. If either option is omitted it will use the current path, or default message.

mixed.test(options: object): Schema

Alternative test(..) signature. options is an object containing some of the following options:

Options = {
  // unique name identifying the test
  name: string;
  // test function, determines schema validity
  test: (value: any) => boolean;
  // the validation error message
  message: string;
  // values passed to message for interpolation
  params: ?object;
  // mark the test as exclusive, meaning only one of the same can be active at once
  exclusive: boolean = false;
}

In the case of mixing exclusive and non-exclusive tests the following logic is used. If a non-exclusive test is added to a schema with an exclusive test of the same name the exclusive test is removed and further tests of the same name will be stacked.

If an exclusive test is added to a schema with non-exclusive tests of the same name the previous tests are removed and further tests of the same name will replace each other.

let max = 64;
let schema = yup.mixed().test({
  name: 'max',
  exclusive: true,
  params: { max },
  message: '${path} must be less than ${max} characters',
  test: (value) => value == null || value.length <= max,
});

mixed.transform((currentValue: any, originalValue: any) => any): Schema

Adds a transformation to the transform chain. Transformations are central to the casting process, default transforms for each type coerce values to the specific type (as verified by isType()). transforms are run before validations and only applied when the schema is not marked as strict (the default). Some types have built in transformations.

Transformations are useful for arbitrarily altering how the object is cast, however, you should take care not to mutate the passed in value. Transforms are run sequentially so each value represents the current state of the cast, you can use the originalValue param if you need to work on the raw initial value.

let schema = string().transform(function (value, originalvalue) {
  return this.isType(value) && value !== null ? value.toUpperCase() : value;
});

schema.cast('jimmy'); // => 'JIMMY'

Each types will handle basic coercion of values to the proper type for you, but occasionally you may want to adjust or refine the default behavior. For example, if you wanted to use a different date parsing strategy than the default one you could do that with a transform.

module.exports = function (formats = 'MMM dd, yyyy') {
  return date().transform(function (value, originalValue) {
    // check to see if the previous transform already parsed the date
    if (this.isType(value)) return value;

    // the default coercion failed so let's try it with Moment.js instead
    value = Moment(originalValue, formats);

    // if it's valid return the date object, otherwise return an `InvalidDate`
    return value.isValid() ? value.toDate() : new Date('');
  });
};

string

Define a string schema. Supports all the same methods as mixed.

let schema = yup.string();

await schema.isValid('hello'); // => true

By default, the cast logic of string is to call toString on the value if it exists. empty values are not coerced (use ensure() to coerce empty values to empty strings).

Failed casts return the input value.

string.required(message?: string | function): Schema

The same as the mixed() schema required, except that empty strings are also considered 'missing' values.

string.length(limit: number | Ref, message?: string | function): Schema

Set a required length for the string value. The ${length} interpolation can be used in the message argument

string.min(limit: number | Ref, message?: string | function): Schema

Set a minimum length limit for the string value. The ${min} interpolation can be used in the message argument

string.max(limit: number | Ref, message?: string | function): Schema

Set a maximum length limit for the string value. The ${max} interpolation can be used in the message argument

string.matches(regex: Regex, message?: string | function): Schema

Provide an arbitrary regex to match the value against.

let schema = string().matches(/(hi|bye)/);

await schema.isValid('hi'); // => true
await schema.isValid('nope'); // => false

string.matches(regex: Regex, options: { message: string, excludeEmptyString: bool }): Schema

An alternate signature for string.matches with an options object. excludeEmptyString, when true, short circuits the regex test when the value is an empty string

let schema = string().matches(/(hi|bye)/, { excludeEmptyString: true });

await schema.isValid(''); // => true

string.email(message?: string | function): Schema

Validates the value as an email address via a regex.

string.url(message?: string | function): Schema

Validates the value as a valid URL via a regex.

string.uuid(message?: string | function): Schema

Validates the value as a valid UUID via a regex.

string.ensure(): Schema

Transforms undefined and null values to an empty string along with setting the default to an empty string.

string.trim(message?: string | function): Schema

Transforms string values by removing leading and trailing whitespace. If strict() is set it will only validate that the value is trimmed.

string.lowercase(message?: string | function): Schema

Transforms the string value to lowercase. If strict() is set it will only validate that the value is lowercase.

string.uppercase(message?: string | function): Schema

Transforms the string value to uppercase. If strict() is set it will only validate that the value is uppercase.

number

Define a number schema. Supports all the same methods as mixed.

let schema = yup.number();

await schema.isValid(10); // => true

The default cast logic of number is: parseFloat.

Failed casts return NaN.

number.min(limit: number | Ref, message?: string | function): Schema

Set the minimum value allowed. The ${min} interpolation can be used in the message argument.

number.max(limit: number | Ref, message?: string | function): Schema

Set the maximum value allowed. The ${max} interpolation can be used in the message argument.

number.lessThan(max: number | Ref, message?: string | function): Schema

Value must be less than max. The ${less} interpolation can be used in the message argument.

number.moreThan(min: number | Ref, message?: string | function): Schema

Value must be strictly greater than min. The ${more} interpolation can be used in the message argument.

number.positive(message?: string | function): Schema

Value must be a positive number.

number.negative(message?: string | function): Schema

Value must be a negative number.

number.integer(message?: string | function): Schema

Validates that a number is an integer.

number.truncate(): Schema

Transformation that coerces the value to an integer by stripping off the digits to the right of the decimal point.

number.round(type: 'floor' | 'ceil' | 'trunc' | 'round' = 'round'): Schema

Adjusts the value via the specified method of Math (defaults to 'round').

boolean

Define a boolean schema. Supports all the same methods as mixed.

let schema = yup.boolean();

await schema.isValid(true); // => true

date

Define a Date schema. By default ISO date strings will parse correctly, for more robust parsing options see the extending schema types at the end of the readme. Supports all the same methods as mixed.

let schema = yup.date();

await schema.isValid(new Date()); // => true

The default cast logic of date is pass the value to the Date constructor, failing that, it will attempt to parse the date as an ISO date string.

Failed casts return an invalid Date.

date.min(limit: Date | string | Ref, message?: string | function): Schema

Set the minimum date allowed. When a string is provided it will attempt to cast to a date first and use the result as the limit.

date.max(limit: Date | string | Ref, message?: string | function): Schema

Set the maximum date allowed, When a string is provided it will attempt to cast to a date first and use the result as the limit.

array

Define an array schema. Arrays can be typed or not, When specifying the element type, cast and isValid will apply to the elements as well. Options passed into isValid are passed also passed to child schemas. Supports all the same methods as mixed.

let schema = yup.array().of(yup.number().min(2));

await schema.isValid([2, 3]); // => true
await schema.isValid([1, -24]); // => false

schema.cast(['2', '3']); // => [2, 3]

You can also pass a subtype schema to the array constructor as a convenience.

array().of(yup.number());
// or
array(yup.number());

The default cast behavior for array is: JSON.parse

Failed casts return: null;

array.of(type: Schema): Schema

Specify the schema of array elements. of() is optional and when omitted the array schema will not validate its contents.

array.length(length: number | Ref, message?: string | function): Schema

Set a specific length requirement for the array. The ${length} interpolation can be used in the message argument.

array.min(limit: number | Ref, message?: string | function): Schema

Set a minimum length limit for the array. The ${min} interpolation can be used in the message argument.

array.max(limit: number | Ref, message?: string | function): Schema

Set a maximum length limit for the array. The ${max} interpolation can be used in the message argument.

array.ensure(): Schema

Ensures that the value is an array, by setting the default to [] and transforming null and undefined values to an empty array as well. Any non-empty, non-array value will be wrapped in an array.

array().ensure().cast(null); // => []
array().ensure().cast(1); // => [1]
array().ensure().cast([1]); // => [1]

array.compact(rejector: (value) => boolean): Schema

Removes falsey values from the array. Providing a rejecter function lets you specify the rejection criteria yourself.

array().compact().cast(['', 1, 0, 4, false, null]); // => [1, 4]

array()
  .compact(function (v) {
    return v == null;
  })
  .cast(['', 1, 0, 4, false, null]); // => ['', 1, 0, 4, false]

object

Define an object schema. Options passed into isValid are also passed to child schemas. Supports all the same methods as mixed.

yup.object().shape({
  name: string().required(),
  age: number().required().positive().integer(),
  email: string().email(),
  website: string().url(),
});

You can also pass a shape to the object constructor as a convenience.

object().shape({
  num: number(),
});
// or
object({
  num: number(),
});

The default cast behavior for object is: JSON.parse

Failed casts return: null;

Object schema defaults

Object schema come with a default value already set, which "builds" out the object shape, a sets any defaults for fields:

const schema = object({
  name: string().default(''),
});

schema.default(); // -> { name: '' }

This may be a bit suprising, but is generally very helpful since it allows large, nested schema to create default values that fill out the whole shape and not just the root object. There is one gotcha! though. For nested object schema that are optional but include non optional fields may fail in unexpected ways:

const schema = object({
  id: string().required(),
  names: object({
    first: string().required(),
  }),
});

schema.isValid({ id: 1 }); // false! names.first is required

This is because yup casts the input object before running validation which will produce:

{ id: '1', names: { first: undefined }}

During the validation phase names exists, and is validated, finding names.first missing. If you wish to avoid this behavior do one of the following:

  • Set the nested default to undefined: names.default(undefined)
  • mark it nullable and default to null: names.nullable().default(null)

object.shape(fields: object, noSortEdges?: Array<[string, string]>): Schema

Define the keys of the object and the schemas for said keys.

Note that you can chain shape method, which acts like object extends, for example:

object({
  a: string(),
  b: number(),
}).shape({
  b: string(),
  c: number(),
});

would be exactly the same as:

object({
  a: string(),
  b: string(),
  c: number(),
});

object.pick(keys: string[]): Schema

Create a new schema from a subset of the original's fields.

const person = object({
  age: number().default(30).required(),
  name: string().default('pat').required(),
  color: string().default('red').required(),
});

const nameAndAge = person.pick(['name', 'age']);
nameAndAge.getDefault(); // => { age: 30, name: 'pat'}

object.omit(keys: string[]): Schema

Create a new schema with fields omitted.

const person = object({
  age: number().default(30).required(),
  name: string().default('pat').required(),
  color: string().default('red').required(),
});

const nameAndAge = person.omit(['color']);
nameAndAge.getDefault(); // => { age: 30, name: 'pat'}

object.getDefaultFromShape(): Record<string, unknown>

Produces a default object value by walking the object shape and calling default() on each field. This is the default behavior of getDefault() but allows for building out an object skeleton regardless of the default().

object.from(fromKey: string, toKey: string, alias: boolean = false): this

Transforms the specified key to a new key. If alias is true then the old key will be left.

let schema = object({
  myProp: mixed(),
  Other: mixed(),
})
  .from('prop', 'myProp')
  .from('other', 'Other', true);

schema.cast({ prop: 5, other: 6 }); // => { myProp: 5, other: 6, Other: 6 }

object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema

Validate that the object value only contains keys specified in shape, pass false as the first argument to disable the check. Restricting keys to known, also enables stripUnknown option, when not in strict mode.

object.camelCase(): Schema

Transforms all object keys to camelCase

object.constantCase(): Schema

Transforms all object keys to CONSTANT_CASE.

Comments
  • Unable to resolve

    Unable to resolve "@babel/runtime/helpers/builtin/interopRequireDefault"

    Hi guys, today I update yup to 0.25.0 and im getting the following error:

    Unable to resolve "@babel/runtime/helpers/builtin/interopRequireDefault" from "node_modules/yup/lib/index.js"
    Failed building JavaScript bundle.
    

    Rolling back to v0.24.1 fix the issue, any advice?

    opened by outaTiME 42
  • Number but allow empty string

    Number but allow empty string

    I have a legacy project where a field can be a number, but the absence of value is defined by an empty string (instead of null).

    I am looking for something like nullable (yup.number().allowEmptyString()) or yup.mixed.oneOf(['', yup.number()]

    Does anyone have an idea on how to handle this in the cleanest way?

    Currently I am doing

    const schema = yup
      .mixed()
      .test(fn) // value === '' || yup.number().isValidSync(value)
      .transform(fn) // value === '' ? value : +value
    

    but this loses beautiful syntax of Yup and just feels dirty since yup.number() is burried inside test, but may be further customized for other fields.

    Thanks

    opened by iamvanja 35
  • How to create a

    How to create a "Dictionary" in a schema?

    Hi! Thanks so much for this library! It's a great help with keeping my data consistent across my app stack.

    I am working on creating schemas to represent objects in a Firebase Realtime Database, which doesn't really use arrays for lists of data, but rather dictionaries using uuids as keys. I'm trying to create a simple way of representing these hash-mapped lists of data in my schemas. I've been working with the suggestions brought up in https://github.com/jquense/yup/issues/130 but have been unsuccessful in getting it to correctly unpack the values.

    Here is my current code:

    export interface Thread extends FirebaseObject {
      uuid: string;
      unread?: number;
      last: Message;
      members: { [uuid: string]: string };
      messages: { [uuid: string]: Message };
      threadName?: string;
    }
    
    export const threadSchema: yup.Schema<Thread> = yup.object().shape({
      uuid: yup.string().required(),
      unread: yup
        .number()
        .positive()
        .integer(),
      last: messageSchema,
      members: yup.lazy(obj => {
        return yup.object(
          Object.keys(obj).map(key => {
            return yup.object({
              [`${key}`]: yup.string(),
            });
          })
        );
      }),
      messages: yup.lazy(obj =>
        yup.object(
          Object.keys(obj).map(key => {
            return yup.object({
              [`${key}`]: messageSchema,
            });
          })
        )
      ),
      threadName: yup.string(),
    });
    
    

    Here is a sample "Thread" object:

    {
        "uuid": "demo",
        "unread": 0,
        "last": { ... },
        "members": {
            "someUniqueId": "Hank",
            "anotherUniqueId": "Henry"
        },
        "messages": {
            "messageID1": { ... }
            "messageID2": { ... }
        },
        "threadName": "Hank and Henry"
    }
    
    opened by mversteeg3 33
  • There is a way to validate that object property is unique in array?

    There is a way to validate that object property is unique in array?

    Schema:

    const headersSchema = yup.array().of(yup.object().shape({
        name:  yup.string(),
        value: yuo.string(),
    }))
    

    I need validate all objects in headers array has unique name property. Please help me solve this problem :)

    opened by lizarusi 31
  • Validate at least one checkbox (boolean) is chosen

    Validate at least one checkbox (boolean) is chosen

    checkbox_fields_-_dummy_com
    const schema = yup.object({
      red: yup.boolean(),
      orange: yup.boolean(),
      green: yup.boolean()
    })
    

    Implementing react-formal-material-ui, and trying to figure out how to require at least one of the options is chosen. How would you go about doing this? If it is custom/external, how to inject the validation error back into the react-formal form?

    opened by rosskevin 30
  • Create dynamic validation schema.

    Create dynamic validation schema.

    I have a JSON from which I'm trying to create dynamic form and validation. I'm using Yup along with Formik. I want to create the validation schema after iterating over the form elements validation key but I'm not sure how to do this.

    export const formData = [
      {
        id: "name",
        label: "Full name",
        placeholder: "Enter full name",
        type: "text",
        required: true,
        value: "User name",
        values: [],
        validations: [
          {
            type: "minLength",
            value: "5",
            error_message: "name should be atleast 5 char long"
          },
          {
            type: "maxLength",
            value: "10",
            error_message: "name should be atleast 5 char long"
          }
        ]
      },
      {
        id: "email",
        label: "Email",
        placeholder: "Email",
        type: "text",
        required: true,
        value: "email",
        values: [],
        validations: [
          {
            type: "minLength",
            value: "5",
            error_message: "name should be atleast 5 char long"
          },
          {
            type: "maxLength",
            value: "10",
            error_message: "name should be atleast 5 char long"
          },
          {
            type: "email",
            error_message: "Valid email"
          }
        ]
      },
      {
        id: "phoneNumber",
        label: "phone number",
        type: "text",
        required: true,
        value: "7878787878",
        values: [],
        validations: [
          {
            type: "minLength",
            value: "5",
            error_message: "name should be atleast 5 char long"
          },
          {
            type: "maxLength",
            value: "10",
            error_message: "name should be atleast 5 char long"
          },
          {
            type: "required",
            error_message: "phone number is required"
          }
        ]
      },
      {
        id: "total",
        label: "Total People in Family",
        placeholder: "family members count",
        type: "text",
        required: false,
        value: "1",
        values: [],
        validations: [
          {
            type: "minLength",
            value: "1",
            error_message: "there should be atleast 1 family member"
          },
          {
            type: "maxLength",
            value: "5",
            error_message: "max family members can be 5"
          }
        ]
      }
    ]
    

    I want to create something like after iterating over the validation but I'm unable to do so.

    let validateSchema = yup.object().shape({
        name: yup.string().required("name is required"),
        email: yup.string().email(),
        phoneNumber: yup.number().min(10, "minium 10 numbers"),
        total: yup
          .number()
          .min(1, "minium 1 member")
          .max(5, "max 5 member")
          .required("member is required")    
    })
    

    Which I will send to Formik. I looked into many links, even posted on SO but couldn't find any useful lead.

    Codesandbox

    opened by vijayranghar 29
  • Pluggable i18n locale/translate

    Pluggable i18n locale/translate

    I see the locale messages in https://github.com/jquense/yup/blob/master/src/locale.js

    But there doesn't appear to be any way to alter these messages, nor a way to plug-in other i18n solutions by referring simply to keys and allowing it to resolve independently.

    Is this something others are needing too?

    opened by rosskevin 29
  • Performance issues for backend use

    Performance issues for backend use

    Hi.

    I love the API of yup, and I love it SO much that I'm trying to build an isomorphic application that uses the same yup schema validation in both the frontend and the backend 🚀.

    However, at the moment it's not really viable simply due to the fact that it's about one order of magnitude slower than it should be, considering what is actually running in the source code ("joi-lite"). I really don't expect ajv levels of "extreme" performance since yup is not compiling schemas, but I'd expect to at least be somewhere around joi's level.

    First, the setup: https://github.com/JaneJeon/validator-benchmark (I forked https://github.com/icebob/validator-benchmark, updated the dependencies, added strict mode to the yup schema, and properly ran the async validator - I tested synchronous version, and it was literally 2x slower xP)

    And, the results (run node .): https://i.imgur.com/AuQLnTD.png

    At first, I thought it was because the email/url regex yup was using was ungodly (no offense), so I thought maybe using simplified regex (like here: https://github.com/icebob/fastest-validator/blob/master/lib/rules/email.js) might help.

    It didn't.

    I changed the regex in both lib/string.js, es/string.js in node_modules/ and it didn't affect the performance in any noticeable way.

    I messed around with sync vs. async, different ways of running the validation, etc.

    Then I realized there's babel's crufty code all over the place, in both es/ and lib/. I dug around and found that babel-compiled code often has performance issues, especially regarding ES6 syntaxes.

    I suspect that the default babel compilation is what's slowing down your codebase significantly since, as far as I can tell, you're only using babel for ESM syntax.

    Is there any way you could directly expose the ESM code or at least use a much more efficient compiler/babel setting? People running this in node/backend just need to convert ESM -> CJS. I feel like babel config is key here.

    I'd really love to use this in my backend and bring it to production! This is the last thing that's preventing me.

    Thanks

    opened by JaneJeon 28
  • How to conditionally validate at least one of n values are set?

    How to conditionally validate at least one of n values are set?

    I have 5 form fields, at last one of which must be populated for validation to succeed. I attempted this, but soon ran into cyclic reference issues:

    tag_business: yup.string().when(['tag_cost_centre', 'tag_project_code', 'tag_internal_order', 'tag_business'], {
      is: (tag_cost_centre, tag_project_code, tag_internal_order, tag_business) => {
        return !(tag_cost_centre, tag_project_code, tag_internal_order, tag_business)
      },
      then: yup.string().required()
    })
    

    What would be the best way to implement this?

    opened by nandastone 28
  • Does anyone have an example of `addMethod` in Typescript?

    Does anyone have an example of `addMethod` in Typescript?

    Context: I'm using Yup with Typescript and formik. Trying to add a custom method to enforce the format of a date value. The Yup README has an example, but I'm not sure how to get it to work with TS. Does anyone have an example? Thanks.

    opened by egmanoj 26
  • Any value that starts with a digit is validated as number

    Any value that starts with a digit is validated as number

    The number validator validates any number that starts with a digit, e.g. 1a is accepted. This seems to be a problem of the value to number conversion:

    let parsed = parseFloat(value);
    if (this.isType(parsed)) return parsed;
    
    return NaN;
    

    From https://www.ecma-international.org/ecma-262/6.0/index.html#sec-parsefloat-string:

    parseFloat may interpret only a leading portion of string as a Number value; it ignores any characters that cannot be interpreted as part of the notation of an decimal literal, and no indication is given that any such characters were ignored.

    What about changing the code to this:

    let parsed = parseFloat(value);
    if (parsed == value) return parsed;
    
    return NaN;
    
    opened by dontub 25
  • allow options for strip invalid value when cast

    allow options for strip invalid value when cast

    • Write a title that summarizes the specific problem
    • Describe what you are trying to accomplish AND what you have tried

    Help Others Reproduce

    export const dayjs_2022_11 = tzDayjs('2022-11-01');
    
    const numberPositive = number().transform(v =>
    {
    	return isUnSafeNumLike(v) ? v : void 0
    }).integer().moreThan(-1)
    
    export function tzDayjsTryValid(inputDate?: ConfigType)
    {
    	let date: Dayjs;
    
    	if (isNum(inputDate) || inputDate)
    	{
    		date = tzDayjs(inputDate);
    	}
    
    	if (!date?.isValid() && typeof inputDate === 'string' && inputDate.length)
    	{
    		date = tzDayjs(inputDate.replace(/^["']+|["']+$/g, ''));
    	}
    
    	return date?.isValid() ? date : null
    }
    
    export const dayRevenueSchema = object({
    	sid: number().concat(numberPositive).positive().required(),
    
    	date: date().transform((currentValue) =>
    	{
    		return tzDayjsTryValid(currentValue)?.toDate()
    	}).test((value) =>
    	{
    		return false
    	}).min(dayjs_2022_11.toDate()).required(),
    
    	cash: number().concat(numberPositive).required().label(EnumRevenueLabel.cash),
    
    	delivery_flow: object({
    		foodpanda: number().concat(numberPositive).label(EnumRevenueLabel.foodpanda),
    		uber: number().concat(numberPositive).label(EnumRevenueLabel.uber),
    		foodpanda2: number().concat(numberPositive).label(EnumRevenueLabel.foodpanda2),
    	}),
    
    	payment_flow: object({
    		jko: number().concat(numberPositive).label(EnumRevenueLabel.jko),
    		ocard: number().concat(numberPositive).label(EnumRevenueLabel.ocard),
    	}),
    
    	other_flow: array(object({
    		key: string().trim().required(),
    		value: number().concat(numberPositive).required(),
    	})),
    
    });
    
    let d = tzDayjs('2022-10-01');
    
    let value = {
    	date: d.toDate(),
    	sid: -1,
    	cash: true,
    }
    
    dayRevenueSchema.cast(value, {
    assert: false
    })
    

    expected

    {
      payment_flow: { ocard: undefined, jko: undefined },
      delivery_flow: { foodpanda2: undefined, uber: undefined, foodpanda: undefined },
      date: undefined,
      sid: undefined
    }
    

    or

    {
      payment_flow: { ocard: undefined, jko: undefined },
      delivery_flow: { foodpanda2: undefined, uber: undefined, foodpanda: undefined },
    }
    

    actual

    image

    opened by bluelovers 0
  • `when` function not working when added dynamically

    `when` function not working when added dynamically

    I'm trying to add the conditionally required function with the when function. When writing tests it works, so I'm thinking that it has to do with rendering of some sorts. I have a multi-step form, but the questions which are depended on each other are in the same step.

    I'm working with VueJs 3 and Vite is there a possibility that any of this has anything to do with it? If i log the schema it says the question is depended, but the validation doesn't change.

    Update: also using the Form component and at the moment thinking this has something to do with it. The validation updates, but the schema doesn't.

    Reproduced it via test here https://codesandbox.io/s/yup-test-case-gg1g1?file=/src/index.test.js

    opened by janesssa 0
  • Why does `SchemaOf` change the resulting type of `schema.cast`?

    Why does `SchemaOf` change the resulting type of `schema.cast`?

    In the following examples I try to determine the type of user.name by casting a simple schema.

    In the first example, the FormValues type is asserted by Yup. In the second example, I use SchemaOf with the FormValues type already defined.

    Since both FormValues types are the same, I expected the resulting type of schema.cast to also be the same. But in the second example, name could also be null or undefined, which makes no sense to me. Could you please help me or explain this behavior? Thank you!

    Using Yup version 0.32.11

    Example 1 with Asserts

    const schema = Yup.object({
      name: Yup.string().default('')
    })
    
    type FormValues = Yup.Asserts<typeof schema>
    
    const user = schema.cast({ name: 'John' })
    type nameType = typeof user.name
    

    Result:

    type FormValues = { name: string }
    type nameType = string
    

    Example 2 with SchemaOf

    type FormValues = {
      name: string
    }
    
    const schema: Yup.SchemaOf<FormValues> = Yup.object({
      name: Yup.string().default(''),
    })
    
    const user = schema.cast({ name: 'John' })
    type nameType = typeof user.name
    

    Result:

    type nameType = string | null | undefined
    
    opened by lukasvice 0
  • feat: add schema.asContext and array.indexContextKey

    feat: add schema.asContext and array.indexContextKey

    This is a proof of concept to give more access in the schema to the parent/any element in the schema. Adds two new methods:

    • schema.asContext

    Adds the whole value as a context, this allows to access any random property of the schema from any node or sub property.

    Eg

    let schema = object()
      .asContext('root')
      .shape({
        foo: number(),
        inner: object({
          bar: number().moreThan(ref('$root.foo')),
        }),
      });
    
    schema.isValid({ foo: 3, inner: { bar: 5 } }); // true
    schema.isValid({ foo: 3, inner: { bar: 2 } }); // false
    
    • array.indexContextKey

    Adds the current element index as a context value, this is useful when comparing the element to previous elements in the array

    Eg to make sure array is sorted

    const schema = array()
      .asContext('root')
      .indexContextKey('idx')
      .of(
        number().when(['$root', '$idx'], ([root, idx], schema) =>
          idx > 0 ? schema.moreThan(root[idx - 1]) : schema,
        ),
      );
    
    schema.isValid([1, 2, 3]); // true
    schema.isValid([1, 3, 2]); // false
    
    
    opened by danielpza 1
  • How to forbid conditional properties?

    How to forbid conditional properties?

    I want to have a conditional property in an object, that must not be present, if the precondition is not met. I don't want it to be optional, but actually forbidden to be present. Is there a better way than using yup.string().oneOf([undefined])?

    What I came up with:

    const schema = yup
      .object({
        source: yup.bool(),
        dependent: yup.string().when("source", {
          is: true,
          then: yup.string().required(),
          otherwise: yup.string().oneOf([undefined])
        })
      })
      .strict()
      .noUnknown(true)
      .required();
    

    Help Others Reproduce

    Reproduction: https://codesandbox.io/s/epic-rgb-zlo1s7?file=/src/index.test.js

    opened by manuschillerdev 0
  • Incorrect date validation for `0` input

    Incorrect date validation for `0` input

    Describe the bug

    I am wanting to validate that a date is between 1980 and today with the following schema

     yup
        .date()
        .typeError()
        .required()
        .min(new Date(1980, 0, 1))
        .max(new Date()),
    

    In our test suite some random inputs like 0 and 100 are used. And oddly enough, 0 parses to a valid date 2000-01-01T07:00:00.000Z. Quite a few more values validate as well like -1 -2 -3 etc.

    To Reproduce

    https://codesandbox.io/s/elated-tdd-1kdgul?file=/src/index.test.js

    Expected behavior

    I expect any value below 1980 to fail validation.

    Platform (please complete the following information):

    • Browser [e.g. chrome, safari]: Chrome
    • Version [e.g. 22]: 108
    opened by steveoh 0
Releases(v1.0.0-beta.7)
  • v1.0.0-beta.7(Aug 22, 2022)

  • v1.0.0-beta.5(Aug 22, 2022)

    Beta 5 fixes partial and deepPartial making it work correctly with lazy schema. Specifically the optionality is added after lazy is evaluated but before any other when conditions are added. This makes it consistent with other conditional schema, where runtime conditions always supersede previous schema configuration. This allows for optional overrides if necessary.

    const person = object({
      name: string().required(),
      age: number().required(),
      legalGuardian:  string().when('age', {
        is: (age) => age != null && age < 18,
        then: (schema) => schema.required(),
      }),
    });
    
    const optionalPerson = person.partial()
    
    person.cast({name: 'James', age: 6 }) // => TypeError legalGuardian required
    
    // age is still required b/c it's applied after the `partial`
    optionalPerson.cast({name: 'James',  age: 6 }) // => TypeError legalGuardian required
    

    This works slightly differently for lazy which have no schema to "start" with:

    const config = object({
      nameOrIdNumber:  lazy((value) => {
         if (typeof value === 'number') return number().required()
         return string().required()
      }),
    });
    
    const opti = config.partial()
    
    config.cast({}) // => TypeError nameOrIdNumber is required
    
    config.partial().cast({}) // => {}
    

    Cast optionality migration path

    A larger breaking change in v1 is the assertion of optionality during cast, making previous patterns like string().nullable().required() no longer possible. Generally this pattern is used when deserialized data is not valid to start, but will become valid through user input such as with an HTML form. v1 no longer allows this, but in order to make migration easier we've added an option to cast that mimics the previous behavior (not exactly but closely).

    const name = string().required()
    
    name.cast(null, { assert: 'ignore-optionality'}) // => null
    

    We recommend updating your schema to new patterns where possible but this allows for incremental upgrades

    What's Changed

    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1630
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1652
    • fix(docs): correct typo "coarce" to "coerce" by @eunicode in https://github.com/jquense/yup/pull/1654
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1656
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1665
    • Capitalize two words in README by @Glitchy-Tozier in https://github.com/jquense/yup/pull/1676
    • Fix typo by @karlhorky in https://github.com/jquense/yup/pull/1672
    • Show example of function message by @karlhorky in https://github.com/jquense/yup/pull/1674
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1678
    • Fix typo: coarce -> coerce by @karlhorky in https://github.com/jquense/yup/pull/1677
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1685
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1692
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1697
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1699
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1709
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1714
    • Fix Typo : delete duplicate word "passed" by @ANTARES-KOR in https://github.com/jquense/yup/pull/1696
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1722
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1727
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1731
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1737
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1744
    • small typo fix by @somodis in https://github.com/jquense/yup/pull/1745
    • feat: better Lazy types and deepPartial fixes by @jquense in https://github.com/jquense/yup/pull/1748
    • feat: add cast nullability migration path. by @jquense in https://github.com/jquense/yup/pull/1749

    New Contributors

    • @eunicode made their first contribution in https://github.com/jquense/yup/pull/1654
    • @Glitchy-Tozier made their first contribution in https://github.com/jquense/yup/pull/1676
    • @karlhorky made their first contribution in https://github.com/jquense/yup/pull/1672
    • @ANTARES-KOR made their first contribution in https://github.com/jquense/yup/pull/1696
    • @somodis made their first contribution in https://github.com/jquense/yup/pull/1745

    Full Changelog: https://github.com/jquense/yup/compare/v1.0.0-beta.4...v1.0.0-beta.5

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.4(Aug 22, 2022)

    What's Changed

    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1611
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1614
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1618
    • chore(deps): update all non-major dependencies by @renovate in https://github.com/jquense/yup/pull/1622

    Full Changelog: https://github.com/jquense/yup/compare/v1.0.0-beta.3...v1.0.0-beta.4

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.3(Mar 9, 2022)

    This release fixes a bug with object().partial where required() schema we're still failing validation. Now they will no longer do that. To enable this fix we made a breaking change to the way that required is implemented.

    • schema.required no longer adds a test named 'required', this state can be determined via the schema spec or describe() metadata
    • String schema now override required directly to add their length check (this test is called required for some ease of back compat)
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.2(Jan 21, 2022)

  • v1.0.0-beta.1(Jan 3, 2022)

    1.0.0-beta.1 (2022-01-03)

    Features

    • flat bundles and size reductions (753abdf)
    • Refactors a number of internal APIs for Schema type implementers, making it easier to consistently create subclasses of Schema.

    BREAKING CHANGES

    • Yup now compiles to a flat bundle using rollup, this gives us faster to parse and smaller code, as well as clearer size insights (10.26kb gzipped!). The possible breaking change is around cherry picking imports, if you are cherry picking imports from yup import string from 'yup/lib/string' this will no longer work. This pattern has not been supported for a long time and could cause larger than necessary bundles.
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-beta.0(Dec 29, 2021)

    1.0.0-beta.0 (2021-12-29)

    • feat!: add json() method and remove default object/array coercion (94b73c4)

    Features

    • Make Array generic consistent with others (a82353f)

    BREAKING CHANGES

    • types only, ArraySchema initial generic is the array type not the type of the array element. array<T>() is still the inner type.
    • object and array schema no longer parse JSON strings by default, nor do they return null for invalid casts.
    object().json().cast('{}')
    array().json().cast('[]')
    

    to mimic the previous behavior

    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-alpha.4(Dec 29, 2021)

    Bug Fixes

    Features

    BREAKING CHANGES

    • mixed schema are no longer treated as the base class for other schema types. It hasn't been for a while, but we've done some nasty prototype slinging to make it behave like it was. Now typescript types should be 1 to 1 with the actual classes yup exposes.

    In general this should not affect anything unless you are extending (via addMethod or otherwise) mixed prototype.

    import {
    -  mixed,
    +  Schema,
    } from 'yup';
    
    - addMethod(mixed, 'method', impl)
    + addMethod(Schema, 'method', impl)
    
    • concat works shallowly now. Previously concat functioned like a deep merge for object, which produced confusing behavior with incompatible concat'ed schema. Now concat for objects works similar to how it works for other types, the provided schema is applied on top of the existing schema, producing a new schema that is the same as calling each builder method in order
    • The function version of when() has been changed to make it easier to type. values are always passed as an array and schema, and options always the second and third argument. this is no longer set to the schema instance. and all functions must return a schema to be type safe
     string()
    -   .when('other', function (other) => {
    -      if (other) return this.required()
    +   .when('other', ([other], schema) => {
    +     return other ? schema.required() : schema
      })
    
    Source code(tar.gz)
    Source code(zip)
  • v1.0.0-alpha.3(Dec 29, 2021)

    Changes the object generics to store the derived type instead of the object shape. This imposes a few limitations on type accuracy at the edges, but dramatically speeds up the type processing by tsc on the command line and in editor. It also removes the need for the SchemaOf helper which worked...poorly.

    Instead the ObjectSchema class accepts a plain type as its first generic:

    interface Folder {
      id: ObjectId,
      label: string,
      files?: File[]
    }
    
    - const folder: SchemaOf<Folder, ObjectId | File> = object({
    -   id: mixed<ObjectId>().defined(),
    -   label: string().defined(), 
    -   files: array(mixed<File>().defined()) 
    - })
    + const folder: ObjectSchema<Folder> = object({ 
    +  id: mixed<ObjectId>().defined(),
    +  label: string().defined(), 
    +  files: array(mixed<File>().defined())
    + })
    

    It's a small diff, but big improvement in type accuracy and usability, especially with custom schema for class instances.

    Note that the generics on the object() factory method are still the "object shape, meaning object<Folder>() won't work as expected. This is a compromise between the two strategies for handling generics and allows for an accurate type on object().getDefault()

    A number of the improvements here are made possible by simplifications to yup's API and logic, this introduces a few breaking changes though most are small and easily migrated from.

    Nullability and presence

    This is the largest, and likely most disruptive change. Prior yup allowed for patterns like:

    const nullableRequiredString = string().nullable().required()
    
    nullableRequiredString.cast(null) // -> null
    
    nullableRequiredString.validate(null) // ValidationError("this is required and cannot be null")
    

    This may seem unintuitive behavior (and it is) but allowed for a common client side validation case, where we want to use a single schema to parse server data, as well as validate user input. In other words, a server might return invalid "default" values that should still fail when trying to submit.

    Now, nullable(), defined and required are all mutually dependent methods. Meaning string().nullable().defined().required() produces a schema where the value must be a string, and not null or undefined. The effect of this is that the type of a cast() is now accurate and the same as the type returned from validate.

    Source code(tar.gz)
    Source code(zip)
  • v0.19.0(Jun 24, 2016)

    Type casts no longer "succeed without fail". For instance boolean will throw if a cast produces an invalid type, instead of quietly coercing to false. By default cast will now throw in these situations, passing assert: false to cast options will disable this behavior and the value returned will be the invalid value (NaN, InvalidDate, null) or the original value if no good invalid value exists in the language

    number().cast('foo', { assert: false }) // -> NaN
    bool().cast('foo', { assert: false })   // -> 'foo'
    
    Source code(tar.gz)
    Source code(zip)
Owner
Jason Quense
Jason Quense
Schema-Inspector is an JSON API sanitisation and validation module.

Schema-Inspector is a powerful tool to sanitize and validate JS objects. It's designed to work both client-side and server-side and to be scalable wit

null 494 Oct 3, 2022
TypeScript-first schema validation for h3 and Nuxt applications

h3-zod Validate h3 and Nuxt 3 requests using zod schema's. Install npm install h3-zod Usage import { createServer } from 'http' import { createApp } f

Robert Soriano 48 Dec 28, 2022
Schema validation utilities for h3, using typebox & ajv

h3-typebox JSON schema validation for h3, using typebox & ajv. Install # Using npm npm install h3-typebox # Using yarn yarn install h3-typebox # Usi

Kevin Marrec 43 Dec 10, 2022
FieldVal - multipurpose validation library. Supports both sync and async validation.

FieldVal-JS The FieldVal-JS library allows you to easily validate data and provide readable and structured error reports. Documentation and Examples D

null 137 Sep 24, 2022
A simple credit cards validation library in JavaScript

creditcard.js A simple credit cards validation library in JavaScript. Project website: https://contaazul.github.io/creditcard.js Install creditcard.js

ContaAzul 323 Jan 7, 2023
Simple, smart and pleasant validation solution.

nice-validator Simple, smart and pleasant validation solution. Download the latest release or install package via npm or bower $ npm install nice-vali

Jony 608 Nov 18, 2022
Tiny Validator for JSON Schema v4

Tiny Validator (for v4 JSON Schema) Use json-schema draft v4 to validate simple values and complex objects using a rich validation vocabulary (example

Geraint 1.2k Dec 21, 2022
Validate for XML schema and returns all the possible failures

detailed-xml-validator Validate for XML schema and returns all the possible failures Sample Rules file <?xml version = "1.0"?> <students nillable="fa

Natural Intelligence 11 Dec 20, 2022
Validate graphql operations against a schema

@graphql-validate With the power of GraphQL-Tools and GraphQL-JS, we are able to provide a smooth experience for validation your GraphQL operations du

Saihajpreet Singh 13 Dec 23, 2022
Convert JSON examples into JSON schema (supports Swagger 2, OpenAPI 3 and 3.1)

json-to-json-schema Convert JSON examples into JSON schema. Supports JSON Schema draft-05 used in Swagger 2.0 and OpenAPI 3.0 and new draft draft-2020

Redocly 9 Sep 15, 2022
jQuery Validation Plugin library sources

jQuery Validation Plugin - Form validation made easy The jQuery Validation Plugin provides drop-in validation for your existing forms, while making al

null 10.3k Jan 3, 2023
String validation

validator.js A library of string validators and sanitizers. Strings only This library validates and sanitizes strings only. If you're not sure if your

null 20.7k Jan 5, 2023
Lightweight JavaScript form validation library inspired by CodeIgniter.

validate.js validate.js is a lightweight JavaScript form validation library inspired by CodeIgniter. Features Validate form fields from over a dozen r

Rick Harrison 2.6k Dec 15, 2022
Cross Browser HTML5 Form Validation.

Validatr Cross Browser HTML5 Form Validation. Getting Started View the documentation to learn how to use Validatr. Changelog Version 0.5.1 - 2013-03-1

Jay Morrow 279 Nov 1, 2022
jQuery Validation Plugin library sources

jQuery Validation Plugin - Form validation made easy The jQuery Validation Plugin provides drop-in validation for your existing forms, while making al

null 10.3k Jan 3, 2023
jQuery form validation plugin

jQuery.validationEngine v3.1.0 Looking for official contributors This project has now been going on for more than 7 years, right now I only maintain t

Cedric Dugas 2.6k Dec 23, 2022
The most powerful data validation library for JS

joi The most powerful schema description language and data validator for JavaScript. Installation npm install joi Visit the joi.dev Developer Portal f

Sideway Inc. 19.6k Jan 4, 2023
:white_check_mark: Easy property validation for JavaScript, Node and Express.

property-validator ✅ Easy property validation for JavaScript, Node and Express Built on top of validator.js, property-validator makes validating reque

Netto Farah 160 Dec 14, 2022