Repository: jquense/yup
Pull request #446 (2019-02-03 16:26:34)

fix: remove README js inconsistencies
Remove some inconsistencies in readme js snippets.

Most changes:

- let or const to var

- `inst` to `schema`

- \`\`\`javascript to \`\`\`js

- // -> or //=> to // =>

- await for async calls

- space after comment and first letter is small

- blank line between schema creation and usage

- bool() to boolean()



Patch:
diff --git a/README.md b/README.md
index 7b9d6f3..4822a55 100644
--- a/README.md
+++ b/README.md
@@ -56,6 +56,7 @@ json separate from validating it, via the `cast` method.
     - [`mixed.transform((currentValue: any, originalValue: any) => any): Schema`](#mixedtransformcurrentvalue-any-originalvalue-any--any-schema)
   - [string](#string)
     - [`string.required(message?: string | function): Schema`](#stringrequiredmessage-string--function-schema)
+    - [`string.length(limit: number | Ref, message?: string | function): Schema`](#stringlengthlimit-number--ref-message-string--function-schema)
     - [`string.min(limit: number | Ref, message?: string | function): Schema`](#stringminlimit-number--ref-message-string--function-schema)
     - [`string.max(limit: number | Ref, message?: string | function): Schema`](#stringmaxlimit-number--ref-message-string--function-schema)
     - [`string.matches(regex: Regex, message?: string | function): Schema`](#stringmatchesregex-regex-message-string--function-schema)
@@ -122,14 +123,14 @@ When using es module syntax, yup exports everything as a named export
 
 ```js
 import * as yup from 'yup'; // for everything
-// or only what you need
-import { string, object } from 'yup';
+// or
+import { string, object } from 'yup'; // for only what you need
 ```
 
 ```js
-var yup = require('yup');
+let yup = require('yup');
 
-var schema = yup.object().shape({
+let schema = yup.object().shape({
   name: yup.string().required(),
   age: yup
     .number()
@@ -143,7 +144,7 @@ var schema = yup.object().shape({
   }),
 });
 
-//check validity
+// check validity
 schema
   .isValid({
     name: 'jimmy',
@@ -153,7 +154,7 @@ schema
     valid; // => true
   });
 
-//you can try and type cast objects to the defined schema
+// you can try and type cast objects to the defined schema
 schema.cast({
   name: 'jimmy',
   age: '24',
@@ -181,13 +182,14 @@ setLocale({
   },
 });
 
-// Now use Yup schemas AFTER you defined your custom dictionary
-const schema = yup.object().shape({
+// 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.name; // => 'ValidationError'
   err.errors; // => ['Deve ser maior que 18']
 });
 ```
@@ -199,7 +201,7 @@ schema.validate({ name: 'jimmy', age: 11 }).catch(function(err) {
 The module export.
 
 ```js
-var yup = require('yup');
+let yup = require('yup');
 
 yup.mixed;
 yup.string;
@@ -211,6 +213,9 @@ yup.array;
 
 yup.reach;
 yup.addMethod;
+yup.ref;
+yup.lazy;
+yup.setLocale;
 yup.ValidationError;
 ```
 
@@ -222,7 +227,7 @@ For nested schema that need to resolve dynamically, you can provide a `value` an
 a `context` object.
 
 ```js
-var schema = object().shape({
+let schema = object().shape({
   nested: object().shape({
     arr: array().of(object().shape({ num: number().max(4) })),
   }),
@@ -257,16 +262,16 @@ at _validation/cast time_ and supported where specified. Ref's are evaluated in
 the ref value is resolved before the field using the ref (be careful of circular dependencies!).
 
 ```js
-var schema = object({
+let schema = object({
   baz: ref('foo.bar'),
   foo: object({
-    bar: string()
-  })
-  x: ref('$x')
-})
+    bar: string(),
+  }),
+  x: ref('$x'),
+});
 
-inst.cast({ foo: { bar: 'boom' } }, { context: { x: 5 } })
-// { baz: 'boom',  x: 5, { foo: { bar: 'boom' } }, }
+schema.cast({ foo: { bar: 'boom' } }, { context: { x: 5 } });
+// => { baz: 'boom',  x: 5, foo: { bar: 'boom' } }
 ```
 
 #### `yup.lazy((value: any) => Schema): Lazy`
@@ -278,7 +283,7 @@ recursive schema like Trees, for polymophic fields and arrays.
 to `undefined` on the child otherwise the object will infinitely nest itself when you cast it!.
 
 ```js
-var node = object({
+let node = object({
   id: number(),
   child: yup.lazy(() => node.default(undefined)),
 });
@@ -312,10 +317,11 @@ Thrown on failed validations, with the following properties
 
 Creates a schema that matches all types. All types inherit from this base type
 
-```javascript
-var schema = yup.mixed();
+```js
+let schema = yup.mixed();
+
 schema.isValid(undefined, function(valid) {
-  valid; //=> true
+  valid; // => true
 });
 ```
 
@@ -383,7 +389,7 @@ schema.validate({ name: 'jimmy', age: 24 }).then(function(value) {
 });
 
 schema.validate({ name: 'jimmy', age: 'hi' }).catch(function(err) {
-  err.name; // 'ValidationError'
+  err.name; // => 'ValidationError'
   err.errors; // => ['age must be a number']
 });
 ```
@@ -397,7 +403,7 @@ Synchronous validation only works if there are no configured async tests, e.g te
 For instance this will work:
 
 ```js
-schema = number.test(
+let schema = number().test(
   'is-42',
   "this isn't the number i want",
   value => value != 42,
@@ -409,7 +415,7 @@ schema.validateSync(23); // throws ValidationError
 however this will not:
 
 ```js
-schema = number.test('is-42', "this isn't the number i want", value =>
+let schema = number().test('is-42', "this isn't the number i want", value =>
   Promise.resolve(value != 42),
 );
 
@@ -424,10 +430,10 @@ 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.
 
 ```js
-const schema = object({
+let schema = object({
   foo: array().of(
     object({
-      loose: bool(),
+      loose: boolean(),
       bar: string().when('loose', {
         is: true,
         otherwise: s => s.strict(),
@@ -436,13 +442,13 @@ const schema = object({
   ),
 });
 
-const rootValue = {
+let rootValue = {
   foo: [{ bar: 1 }, { bar: 1, loose: true }],
 };
 
-await schema.validateAt('foo[0].bar', rootValue); // -> '1';
+await schema.validateAt('foo[0].bar', rootValue); // => ValidationError: must be a string
 
-await schema.validateAt('foo[1].bar', rootValue); // -> ValidationError. must be a string;
+await schema.validateAt('foo[1].bar', rootValue); // => '1'
 ```
 
 #### `mixed.validateSyncAt(path: string, value: any, options?: object): any`
@@ -491,7 +497,7 @@ let schema = object({
   notThis: string().strip(),
 });
 
-schema.cast({ notThis: 'foo', useThis: 4 }); // { useThis: 4 }
+schema.cast({ notThis: 'foo', useThis: 4 }); // => { useThis: 4 }
 ```
 
 #### `mixed.withMutation(builder: (current: Schema) => void): void`
@@ -523,7 +529,7 @@ object()
 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 an new default.
+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.
 
 ```js
@@ -533,7 +539,7 @@ yup.object.default({ number: 5 }); // object will be cloned every time a default
 
 yup.object.default(() => ({ number: 5 })); // this is cheaper
 
-yup.date.default(() => new Date()); //also helpful for defaults that change over time
+yup.date.default(() => new Date()); // also helpful for defaults that change over time
 ```
 
 #### `mixed.default(): Any`
@@ -563,11 +569,12 @@ be used in the `message` argument.
 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.
 
-```javascript
-var schema = yup.mixed().oneOf(['jimmy', 42]);
-schema.isValid(42); //=> true
-schema.isValid('jimmy'); //=> true
-schema.isValid(new Date()); //=> false
+```js
+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)`
@@ -575,10 +582,11 @@ schema.isValid(new Date()); //=> false
 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.
 
-```javascript
-var schema = yup.mixed().notOneOf(['jimmy', 42]);
-schema.isValid(42); //=> false
-schema.isValid(new Date()); //=> true
+```js
+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`
@@ -593,11 +601,10 @@ 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.
 
-```javascript
-var inst = yup.object({
-  isBig: yup.boolean(),
-  count: yup
-    .number()
+```js
+let schema = object({
+  isBig: boolean(),
+  count: number()
     .when('isBig', {
       is: true, // alternatively: (val) => val == true
       then: yup.number().min(5),
@@ -606,23 +613,23 @@ var inst = yup.object({
     .when('$other', (other, schema) => (other === 4 ? schema.max(6) : schema)),
 });
 
-inst.validate(value, { context: { other: 4 } });
+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.
 
-```javascript
-var inst = yup.object({
-  isSpecial: yup.bool(),
-  isBig: yup.bool(),
-  count: yup.number().when(['isBig', 'isSpecial'], {
+```js
+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),
   }),
 });
 
-inst.validate({
+await schema.validate({
   isBig: true,
   isSpecial: true,
   count: 10,
@@ -633,14 +640,14 @@ Alternatively you can provide a function that returns a schema
 (called with the value of the key and the current schema).
 
 ```js
-var inst = yup.object({
+let schema = yup.object({
   isBig: yup.boolean(),
   count: yup.number().when('isBig', (isBig, schema) => {
     return isBig ? schema.min(5) : schema.min(0);
   }),
 });
 
-inst.validate({ isBig: false, count: 4 });
+await schema.validate({ isBig: false, count: 4 });
 ```
 
 #### `mixed.test(name: string, message: string | function, test: function): Schema`
@@ -662,20 +669,21 @@ the `test` function is called with the current `value`. For more advanced valida
 use the alternate signature to provide more options (see below):
 
 ```js
-var jimmySchema = yup.string()
-  .test('is-jimmy', '${path} is not Jimmy', value => value === 'jimmy');
+let jimmySchema = string().test(
+  'is-jimmy',
+  '${path} is not Jimmy',
+  value => value === 'jimmy',
+);
 
 // or make it async by returning a promise
-var asyncJimmySchema = yup.string()
-  .test('is-jimmy', '${path} is not Jimmy', function (value){
-    return fetch('/is-jimmy/' + value)
-      .then(response => response.responseText === 'true')
-  });
-
-schema.isValid('jimmy').then(...) //=> true
+let asyncJimmySchema = string().test(
+  'is-jimmy',
+  '${path} is not Jimmy',
+  async (value) => (await fetch('/is-jimmy/' + value)).responseText === 'true',
+});
 
-schema.isValid('john').then(...) //=> false
-schema.errors // => [ 'this is not Jimmy!']
+await schema.isValid('jimmy'); // => true
+await schema.isValid('john'); // => false
 ```
 
 test functions are called with a special context, or `this` value, that exposes some useful metadata and functions. Note that to use the `this` context the test function must be a function expression (`function test(value) {}`), not an arrow function, since arrow functions have lexical context.
@@ -694,11 +702,11 @@ Alternative `test(..)` signature. `options` is an object containing some of the
 
 ```js
 Options = {
-  // Unique name identifying the test
+  // unique name identifying the test
   name: string;
   // test function, determines schema validity
   test: (value: any) => boolean;
-  // The validation error message
+  // the validation error message
   message: string;
   // values passed to message for interpolation
   params: ?object;
@@ -714,9 +722,9 @@ 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.
 
-```javascript
-var max = 64;
-var schema = yup.mixed().test({
+```js
+let max = 64;
+let schema = yup.mixed().test({
   name: 'max',
   exclusive: true,
   params: { max },
@@ -735,12 +743,12 @@ Transformations are useful for arbitrarily altering how the object is cast, **ho
 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.
 
-```javascript
-var schema = yup.string().transform(function(currentValue, originalvalue) {
+```js
+let schema = string().transform(function(value, originalvalue) {
   return this.isType(value) && value !== null ? value.toUpperCase() : value;
 });
 
-schema.cast('jimmy'); //=> 'JIMMY'
+schema.cast('jimmy'); // => 'JIMMY'
 ```
 
 Each types will handle basic coercion of values to the proper type for you, but occasionally
@@ -748,25 +756,28 @@ you may want to adjust or refine the default behavior. For example, if you wante
 date parsing strategy than the default one you could do that with a transform.
 
 ```js
-yup.date().transform(function(formats = 'MMM dd, yyyy') {
-  //check to see if the previous transform already parsed the date
-  if (this.isType(value)) return value;
+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 lets try it with Moment.js instead
-  value = Moment(originalValue, formats);
+    // the default coercion failed so lets try it with Moment.js instead
+    value = Moment(originalValue, formats);
 
-  //if its valid return the date object, otherwise return an `InvalidDate`
-  return date.isValid() ? date.toDate() : new Date('');
-});
+    // if its 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`](#mixed).
 
-```javascript
-var schema = yup.string();
-schema.isValid('hello'); //=> true
+```js
+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.
@@ -778,26 +789,27 @@ Failed casts return the input value.
 
 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 an minimum length limit for the string value. The `${min}` interpolation can be used in the `message` argument
+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 an maximum length limit for the string value. The `${max}` interpolation can be used in the `message` argument
+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.
 
-```javascript
-var v = string().matches(/(hi|bye)/);
-v.isValid('hi')
-  .should.eventually()
-  .equal(true);
-v.isValid('nope')
-  .should.eventually()
-  .equal(false);
+```js
+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`
@@ -805,11 +817,10 @@ v.isValid('nope')
 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
 
-```javascript
-var v = string().matches(/(hi|bye)/, { excludeEmptyString: true });
-v.isValid('')
-  .should.eventually()
-  .equal(true);
+```js
+let schema = string().matches(/(hi|bye)/, { excludeEmptyString: true });
+
+await schema.isValid(''); // => true
 ```
 
 #### `string.email(message?: string | function): Schema`
@@ -844,9 +855,10 @@ will only validate that the value is uppercase.
 
 Define a number schema. Supports all the same methods as [`mixed`](#mixed).
 
-```javascript
-var schema = yup.number();
-schema.isValid(10); //=> true
+```js
+let schema = yup.number();
+
+await schema.isValid(10); // => true
 ```
 
 The default `cast` logic of `number` is: [`parseFloat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat).
@@ -898,9 +910,10 @@ Adjusts the value via the specified method of `Math` (defaults to 'round').
 
 Define a boolean schema. Supports all the same methods as [`mixed`](#mixed).
 
-```javascript
-var schema = yup.boolean();
-schema.isValid(true); //=> true
+```js
+let schema = yup.boolean();
+
+await schema.isValid(true); // => true
 ```
 
 ### date
@@ -909,9 +922,10 @@ 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`](#mixed).
 
-```javascript
-var schema = yup.date();
-schema.isValid(new Date()); //=> true
+```js
+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
@@ -935,19 +949,20 @@ Define an array schema. Arrays can be typed or not, When specifying the element
 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`](#mixed).
 
-```javascript
-var schema = yup.array().of(yup.number().min(2));
-schema.isValid([2, 3]); //=> true
-schema.isValid([1, -24]); //=> false
+```js
+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]
+schema.cast(['2', '3']); // => [2, 3]
 ```
 
 You can also pass a subtype schema to the array constructor as a convenience.
 
 ```js
 array().of(yup.number());
-//or
+// or
 array(yup.number());
 ```
 
@@ -966,11 +981,11 @@ The same as the `mixed()` schema required, except that empty arrays are also con
 
 #### `array.min(limit: number | Ref, message?: string | function): Schema`
 
-Set an minimum length limit for the array. The `${min}` interpolation can be used in the `message` argument.
+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 an maximum length limit for the array. The `${max}` interpolation can be used in the `message` argument.
+Set a maximum length limit for the array. The `${max}` interpolation can be used in the `message` argument.
 
 #### `array.ensure(): Schema`
 
@@ -980,29 +995,29 @@ values to an empty array as well. Any non-empty, non-array value will be wrapped
 ```js
 array()
   .ensure()
-  .cast(null); // -> []
+  .cast(null); // => []
 array()
   .ensure()
-  .cast(1); // -> [1]
+  .cast(1); // => [1]
 array()
   .ensure()
-  .cast([1]); // -> [1]
+  .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.
 
-```javascript
+```js
 array()
   .compact()
-  .cast(['', 1, 0, 4, false, null]); // => [1,4]
+  .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]
+  .cast(['', 1, 0, 4, false, null]); // => ['', 1, 0, 4, false]
 ```
 
 ### object
@@ -1010,7 +1025,7 @@ array()
 Define an object schema. Options passed into `isValid` are also passed to child schemas.
 Supports all the same methods as [`mixed`](#mixed).
 
-```javascript
+```js
 yup.object().shape({
   name: string().required(),
   age: number()
@@ -1028,7 +1043,7 @@ You can also pass a shape to the object constructor as a convenience.
 object().shape({
   num: number(),
 });
-//or
+// or
 object({
   num: number(),
 });
@@ -1044,7 +1059,7 @@ 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:
 
-```javascript
+```js
 object({
   a: string(),
   b: number(),
@@ -1056,7 +1071,7 @@ object({
 
 would be exactly the same as:
 
-```javascript
+```js
 object({
   a: string(),
   b: string(),
@@ -1068,15 +1083,15 @@ object({
 
 Transforms the specified key to a new key. If `alias` is `true` then the old key will be left.
 
-```javascript
-var schema = object({
+```js
+let schema = object({
   myProp: mixed(),
   Other: mixed(),
 })
   .from('prop', 'myProp')
   .from('other', 'Other', true);
 
-inst.cast({ prop: 5, other: 6 }); // => { myProp: 5, other: 6, Other: 6 }
+schema.cast({ prop: 5, other: 6 }); // => { myProp: 5, other: 6, Other: 6 }
 ```
 
 #### `object.noUnknown(onlyKnownKeys: boolean = true, message?: string | function): Schema`
@@ -1097,13 +1112,13 @@ Transforms all object keys to CONSTANT_CASE.
 The simplest way to extend an existing type is just to cache a configured schema and use that through your application.
 
 ```js
-var yup = require('yup');
-var parseFormats = ['MMM dd, yyy'];
-var invalidDate = new Date('');
+let yup = require('yup');
+let parseFormats = ['MMM dd, yyy'];
+let invalidDate = new Date('');
 
 module.exports = yup.date().transform(function(value, originalValue) {
   if (this.isType(value)) return value;
-  //the default coercion transform failed so lets try it with Moment instead
+  // the default coercion transform failed so lets try it with Moment instead
   value = Moment(originalValue, parseFormats);
   return value.isValid() ? value.toDate() : invalidDate;
 });
@@ -1125,7 +1140,7 @@ You should keep in mind some basic guidelines when extending schemas
 **Adjust core Types**
 
 ```js
-var invalidDate = new Date('');
+let invalidDate = new Date('');
 
 function parseDateFromFormats(formats, parseStrict) {
   return this.transform(function(value, originalValue) {
@@ -1149,8 +1164,8 @@ utility or pattern that works with that pattern. The below demonstrates using th
 syntax since its less verbose, but you absolutely aren't required to use it.
 
 ```js
-var DateSchema = yup.date;
-var invalidDate = new Date(''); // our failed to coerce value
+let DateSchema = yup.date;
+let invalidDate = new Date(''); // our failed to coerce value
 
 class MomentDateSchemaType extends DateSchema {
   constructor() {
@@ -1180,7 +1195,7 @@ class MomentDateSchemaType extends DateSchema {
   }
 }
 
-var schema = new MomentDateSchemaType();
+let schema = new MomentDateSchemaType();
 
-schema.format('YYYY-MM-DD').cast('It is 2012-05-25'); // Fri May 25 2012 00:00:00 GMT-0400 (Eastern Daylight Time)
+schema.format('YYYY-MM-DD').cast('It is 2012-05-25'); // => Fri May 25 2012 00:00:00 GMT-0400 (Eastern Daylight Time)
 ```
diff --git a/package.json b/package.json
index 5f7f2f6..b2159e8 100644
--- a/package.json
+++ b/package.json
@@ -88,7 +88,7 @@
     "sinon-chai": "^3.2.0"
   },
   "dependencies": {
-    "@babel/runtime": "7.0.0",
+    "@babel/runtime": "^7.0.0",
     "fn-name": "~2.0.1",
     "lodash": "^4.17.10",
     "property-expr": "^1.5.0",
diff --git a/src/Condition.js b/src/Condition.js
index d1ef6ee..9d03437 100644
--- a/src/Condition.js
+++ b/src/Condition.js
@@ -40,13 +40,9 @@ class Conditional {
     }
   }
 
-  getValue(parent, context) {
-    let values = this.refs.map(r => r.getValue(parent, context));
+  resolve(ctx, options) {
+    let values = this.refs.map(ref => ref.getValue(options));
 
-    return values;
-  }
-
-  resolve(ctx, values) {
     let schema = this.fn.apply(ctx, values.concat(ctx));
 
     if (schema !== undefined && !isSchema(schema))
diff --git a/src/Reference.js b/src/Reference.js
index bcb03b0..f82cead 100644
--- a/src/Reference.js
+++ b/src/Reference.js
@@ -1,48 +1,69 @@
 import { getter } from 'property-expr';
 
-let validateName = d => {
-  if (typeof d !== 'string')
-    throw new TypeError("ref's must be strings, got: " + d);
+const prefixes = {
+  context: '$',
+  value: '.',
 };
 
 export default class Reference {
-  static isRef(value) {
-    return !!(value && (value.__isYupRef || value instanceof Reference));
-  }
+  constructor(key, options = {}) {
+    if (typeof key !== 'string')
+      throw new TypeError('ref must be a string, got: ' + key);
 
-  toString() {
-    return `Ref(${this.key})`;
+    this.key = key.trim();
+
+    if (key === '') throw new TypeError('ref must be a non-empty string');
+
+    this.isContext = this.key[0] === prefixes.context;
+    this.isValue = this.key[0] === prefixes.value;
+    this.isSibling = !this.isContext && !this.isValue;
+
+    let prefix = this.isContext
+      ? prefixes.context
+      : this.isValue
+        ? prefixes.value
+        : '';
+
+    this.path = this.key.slice(prefix.length);
+    this.getter = this.path && getter(this.path, true);
+    this.map = options.map;
   }
 
-  constructor(key, mapFn, options = {}) {
-    validateName(key);
-    let prefix = options.contextPrefix || '$';
+  getValue(options) {
+    let result = this.isContext
+      ? options.context
+      : this.isValue
+        ? options.value
+        : options.parent;
 
-    if (typeof key === 'function') {
-      key = '.';
-    }
+    if (this.getter) result = this.getter(result || {});
 
-    this.key = key.trim();
-    this.prefix = prefix;
-    this.isContext = this.key.indexOf(prefix) === 0;
-    this.isSelf = this.key === '.';
+    if (this.map) result = this.map(result);
+
+    return result;
+  }
 
-    this.path = this.isContext ? this.key.slice(this.prefix.length) : this.key;
-    this._get = getter(this.path, true);
-    this.map = mapFn || (value => value);
+  cast(value, options) {
+    return this.getValue({ ...options, value });
   }
+
   resolve() {
     return this;
   }
 
-  cast(value, { parent, context }) {
-    return this.getValue(parent, context);
+  describe() {
+    return {
+      type: 'ref',
+      key: this.key,
+    };
   }
 
-  getValue(parent, context) {
-    let isContext = this.isContext;
-    let value = this._get(isContext ? context : parent || context || {});
-    return this.map(value);
+  toString() {
+    return `Ref(${this.key})`;
+  }
+
+  static isRef(value) {
+    return value && value.__isYupRef;
   }
 }
 
diff --git a/src/mixed.js b/src/mixed.js
index ac45454..f72b49f 100644
--- a/src/mixed.js
+++ b/src/mixed.js
@@ -5,7 +5,7 @@ import toArray from 'lodash/toArray';
 import { mixed as locale } from './locale';
 import Condition from './Condition';
 import runValidations from './util/runValidations';
-import merge from './util/merge';
+import prependDeep from './util/prependDeep';
 import isSchema from './util/isSchema';
 import isAbsent from './util/isAbsent';
 import createValidation from './util/createValidation';
@@ -96,14 +96,15 @@ const proto = (SchemaType.prototype = {
   },
 
   withMutation(fn) {
+    let before = this._mutate;
     this._mutate = true;
     let result = fn(this);
-    this._mutate = false;
+    this._mutate = before;
     return result;
   },
 
   concat(schema) {
-    if (!schema) return this;
+    if (!schema || schema === this) return this;
 
     if (schema._type !== this._type && this._type !== 'mixed')
       throw new TypeError(
@@ -111,23 +112,23 @@ const proto = (SchemaType.prototype = {
           this._type
         } and ${schema._type}`,
       );
-    var cloned = this.clone();
-    var next = merge(this.clone(), schema.clone());
 
-    // undefined isn't merged over, but is a valid value for default
+    var next = prependDeep(schema.clone(), this);
+
+    // new undefined default is overriden by old non-undefined one, revert
     if (has(schema, '_default')) next._default = schema._default;
 
-    next.tests = cloned.tests;
-    next._exclusive = cloned._exclusive;
+    next.tests = this.tests;
+    next._exclusive = this._exclusive;
 
     // manually add the new tests to ensure
     // the deduping logic is consistent
-    schema.tests.forEach(fn => {
-      next = next.test(fn.OPTIONS);
+    next.withMutation(next => {
+      schema.tests.forEach(fn => {
+        next.test(fn.OPTIONS);
+      });
     });
 
-    next._type = schema._type;
-
     return next;
   },
 
@@ -136,11 +137,10 @@ const proto = (SchemaType.prototype = {
     return !this._typeCheck || this._typeCheck(v);
   },
 
-  resolve({ context, parent }) {
+  resolve(options) {
     if (this._conditions.length) {
       return this._conditions.reduce(
-        (schema, match) =>
-          match.resolve(schema, match.getValue(parent, context)),
+        (schema, condition) => condition.resolve(schema, options),
         this,
       );
     }
@@ -149,7 +149,7 @@ const proto = (SchemaType.prototype = {
   },
 
   cast(value, options = {}) {
-    let resolvedSchema = this.resolve(options);
+    let resolvedSchema = this.resolve({ ...options, value });
     let result = resolvedSchema._cast(value, options);
 
     if (
@@ -242,12 +242,12 @@ const proto = (SchemaType.prototype = {
   },
 
   validate(value, options = {}) {
-    let schema = this.resolve(options);
+    let schema = this.resolve({ ...options, value });
     return schema._validate(value, options);
   },
 
   validateSync(value, options = {}) {
-    let schema = this.resolve(options);
+    let schema = this.resolve({ ...options, value });
     let result, err;
 
     schema
@@ -270,7 +270,7 @@ const proto = (SchemaType.prototype = {
 
   isValidSync(value, options) {
     try {
-      this.validateSync(value, { ...options });
+      this.validateSync(value, options);
       return true;
     } catch (err) {
       if (err.name === 'ValidationError') return false;
@@ -299,9 +299,9 @@ const proto = (SchemaType.prototype = {
     return next;
   },
 
-  strict() {
+  strict(isStrict = true) {
     var next = this.clone();
-    next._options.strict = true;
+    next._options.strict = isStrict;
     return next;
   },
 
@@ -315,9 +315,9 @@ const proto = (SchemaType.prototype = {
     return next;
   },
 
-  nullable(value) {
+  nullable(isNullable = true) {
     var next = this.clone();
-    next._nullable = value === false ? false : true;
+    next._nullable = isNullable;
     return next;
   },
 
@@ -382,11 +382,16 @@ const proto = (SchemaType.prototype = {
   },
 
   when(keys, options) {
+    if (arguments.length === 1) {
+      options = keys;
+      keys = '.';
+    }
+
     var next = this.clone(),
       deps = [].concat(keys).map(key => new Ref(key));
 
     deps.forEach(dep => {
-      if (!dep.isContext) next._deps.push(dep.key);
+      if (dep.isSibling) next._deps.push(dep.key);
     });
 
     next._conditions.push(new Condition(deps, options));
@@ -510,3 +515,4 @@ for (const method of ['validate', 'validateSync'])
 
 for (const alias of ['equals', 'is']) proto[alias] = proto.oneOf;
 for (const alias of ['not', 'nope']) proto[alias] = proto.notOneOf;
+proto.optional = proto.notRequired;
diff --git a/src/object.js b/src/object.js
index f8b3f96..fdd5110 100644
--- a/src/object.js
+++ b/src/object.js
@@ -240,11 +240,15 @@ inherits(ObjectSchema, MixedSchema, {
       },
     });
 
-    if (noAllow) next._options.stripUnknown = true;
+    next._options.stripUnknown = noAllow;
 
     return next;
   },
 
+  unknown(allow = true, message = locale.noUnknown) {
+    return this.noUnknown(!allow, message);
+  },
+
   transformKeys(fn) {
     return this.transform(obj => obj && mapKeys(obj, (_, key) => fn(key)));
   },
diff --git a/src/util/createValidation.js b/src/util/createValidation.js
index 3cb4c2c..b46f592 100644
--- a/src/util/createValidation.js
+++ b/src/util/createValidation.js
@@ -68,8 +68,10 @@ export default function createValidation(options) {
     ...rest
   }) {
     let parent = options.parent;
-    let resolve = value =>
-      Ref.isRef(value) ? value.getValue(parent, options.context) : value;
+    let resolve = item =>
+      Ref.isRef(item)
+        ? item.getValue({ value, parent, context: options.context })
+        : item;
 
     let createError = createErrorFactory({
       message,
diff --git a/src/util/merge.js b/src/util/merge.js
deleted file mode 100644
index 79ab8a2..0000000
--- a/src/util/merge.js
+++ /dev/null
@@ -1,30 +0,0 @@
-import has from 'lodash/has';
-import isSchema from './isSchema';
-
-let isObject = obj => Object.prototype.toString.call(obj) === '[object Object]';
-
-export default function merge(target, source) {
-  for (var key in source)
-    if (has(source, key)) {
-      var targetVal = target[key],
-        sourceVal = source[key];
-
-      if (sourceVal === undefined) continue;
-
-      if (isSchema(sourceVal)) {
-        target[key] = isSchema(targetVal)
-          ? targetVal.concat(sourceVal)
-          : sourceVal;
-      } else if (isObject(sourceVal)) {
-        target[key] = isObject(targetVal)
-          ? merge(targetVal, sourceVal)
-          : sourceVal;
-      } else if (Array.isArray(sourceVal)) {
-        target[key] = Array.isArray(targetVal)
-          ? targetVal.concat(sourceVal)
-          : sourceVal;
-      } else target[key] = source[key];
-    }
-
-  return target;
-}
diff --git a/src/util/prependDeep.js b/src/util/prependDeep.js
new file mode 100644
index 0000000..6d00e75
--- /dev/null
+++ b/src/util/prependDeep.js
@@ -0,0 +1,27 @@
+import has from 'lodash/has';
+import isSchema from './isSchema';
+
+let isObject = obj => Object.prototype.toString.call(obj) === '[object Object]';
+
+export default function prependDeep(target, source) {
+  for (var key in source)
+    if (has(source, key)) {
+      var sourceVal = source[key],
+        targetVal = target[key];
+
+      if (targetVal === undefined) {
+        target[key] = sourceVal;
+      } else if (targetVal === sourceVal) {
+        continue;
+      } else if (isSchema(targetVal)) {
+        if (isSchema(sourceVal)) target[key] = sourceVal.concat(targetVal);
+      } else if (isObject(targetVal)) {
+        if (isObject(sourceVal))
+          target[key] = prependDeep(targetVal, sourceVal);
+      } else if (Array.isArray(targetVal)) {
+        if (Array.isArray(sourceVal)) target[key] = sourceVal.concat(targetVal);
+      }
+    }
+
+  return target;
+}
diff --git a/src/util/sortFields.js b/src/util/sortFields.js
index b544d94..6876262 100644
--- a/src/util/sortFields.js
+++ b/src/util/sortFields.js
@@ -23,7 +23,7 @@ export default function sortFields(fields, excludes = []) {
 
       if (!~nodes.indexOf(key)) nodes.push(key);
 
-      if (Ref.isRef(value) && !value.isContext) addNode(value.path, key);
+      if (Ref.isRef(value) && value.isSibling) addNode(value.path, key);
       else if (isSchema(value) && value._deps)
         value._deps.forEach(path => addNode(path, key));
     }


Test patch:
diff --git a/test/mixed.js b/test/mixed.js
index 153f79e..2c62038 100644
--- a/test/mixed.js
+++ b/test/mixed.js
@@ -1,4 +1,15 @@
-import { array, mixed, string, number, object, ref, reach, bool } from '../src';
+import {
+  array,
+  mixed,
+  string,
+  number,
+  object,
+  ref,
+  reach,
+  bool,
+  ValidationError,
+} from '../src';
+
 let noop = () => {};
 
 function ensureSync(fn) {
@@ -510,6 +521,55 @@ describe('Mixed Types ', () => {
       });
   });
 
+  describe('withMutation', () => {
+    it('should pass the same instance to a provided function', () => {
+      let inst = mixed();
+      let func = sinon.spy();
+
+      inst.withMutation(func);
+
+      func.should.have.been.calledOnceWithExactly(inst);
+    });
+
+    it('should temporarily make mutable', () => {
+      let inst = mixed();
+
+      let update = () => {
+        inst.withMutation(inst => {
+          inst.test('a', () => true);
+        });
+      };
+
+      update.should.increase(() => inst.tests.length).by(1);
+    });
+
+    it('should return immutability', () => {
+      let inst = mixed();
+      inst.withMutation(() => {});
+
+      let update = () => {
+        inst.test('a', () => true);
+      };
+
+      update.should.not.increase(() => inst.tests.length);
+    });
+
+    it('should work with nesting', () => {
+      let inst = mixed();
+
+      let update = () => {
+        inst.withMutation(inst => {
+          inst.withMutation(inst => {
+            inst.test('a', () => true);
+          });
+          inst.test('b', () => true);
+        });
+      };
+
+      update.should.increase(() => inst.tests.length).by(2);
+    });
+  });
+
   describe('concat', () => {
     let next;
     let inst = object({
@@ -585,6 +645,14 @@ describe('Mixed Types ', () => {
     }.should.not.throw(TypeError));
   });
 
+  it('concat should validate with mixed and other type', async function() {
+    let inst = mixed().concat(number());
+
+    await inst
+      .validate([])
+      .should.be.rejected(ValidationError, /should be a `number`/);
+  });
+
   it('concat should maintain undefined defaults', function() {
     let inst = string().default('hi');
 
@@ -637,7 +705,7 @@ describe('Mixed Types ', () => {
 
   it('should handle multiple conditionals', function() {
     let called = false;
-    let inst = mixed().when(['prop', 'other'], function(prop, other) {
+    let inst = mixed().when(['$prop', '$other'], function(prop, other) {
       other.should.equal(true);
       prop.should.equal(1);
       called = true;
@@ -646,7 +714,7 @@ describe('Mixed Types ', () => {
     inst.cast({}, { context: { prop: 1, other: true } });
     called.should.equal(true);
 
-    inst = mixed().when(['prop', 'other'], {
+    inst = mixed().when(['$prop', '$other'], {
       is: 5,
       then: mixed().required(),
     });
@@ -701,6 +769,36 @@ describe('Mixed Types ', () => {
     inst.default().should.eql({ prop: undefined });
   });
 
+  it('should support self references in conditions', async function() {
+    let inst = number().when('.', {
+      is: value => value > 0,
+      then: number().min(5),
+    });
+
+    await inst
+      .validate(4)
+      .should.be.rejectedWith(ValidationError, /must be greater/);
+
+    await inst.validate(5).should.be.fulfilled();
+
+    await inst.validate(-1).should.be.fulfilled();
+  });
+
+  it('should support conditional single argument as options shortcut', async function() {
+    let inst = number().when({
+      is: value => value > 0,
+      then: number().min(5),
+    });
+
+    await inst
+      .validate(4)
+      .should.be.rejectedWith(ValidationError, /must be greater/);
+
+    await inst.validate(5).should.be.fulfilled();
+
+    await inst.validate(-1).should.be.fulfilled();
+  });
+
   it('should use label in error message', async function() {
     let label = 'Label';
     let inst = object({
diff --git a/test/object.js b/test/object.js
index 543fd39..2069d51 100644
--- a/test/object.js
+++ b/test/object.js
@@ -329,6 +329,17 @@ describe('Object types', () => {
     ]);
   });
 
+  it('should work with noUnknown override', async () => {
+    let inst = object()
+      .shape({
+        prop: mixed(),
+      })
+      .noUnknown()
+      .noUnknown(false);
+
+    await inst.validate({ extra: 'field' }).should.become({ extra: 'field' });
+  });
+
   it('should strip specific fields', () => {
     let inst = object().shape({
       prop: mixed().strip(false),
diff --git a/test/yup.js b/test/yup.js
index 51f54e5..9ec8d51 100644
--- a/test/yup.js
+++ b/test/yup.js
@@ -1,5 +1,5 @@
 import reach, { getIn } from '../src/util/reach';
-import merge from '../src/util/merge';
+import prependDeep from '../src/util/prependDeep';
 import { settled } from '../src/util/runValidations';
 
 import { object, array, string, lazy, number } from '../src';
@@ -36,11 +36,11 @@ describe('Yup', function() {
     ]);
   });
 
-  it('should merge', function() {
-    var a = { a: 1, b: 'hello', c: [1, 2, 3], d: { a: /hi/ }, e: { b: 5 } };
-    var b = { a: 4, c: [4, 5, 3], d: { b: 'hello' }, f: { c: 5 }, g: null };
+  it('should prepend deeply', function() {
+    var a = { a: 4, c: [4, 5, 3], d: { b: 'hello' }, f: { c: 5 }, g: null };
+    var b = { a: 1, b: 'hello', c: [1, 2, 3], d: { a: /hi/ }, e: { b: 5 } };
 
-    merge(a, b).should.deep.eql({
+    prependDeep(a, b).should.deep.eql({
       a: 4,
       b: 'hello',
       c: [1, 2, 3, 4, 5, 3],
@@ -54,6 +54,17 @@ describe('Yup', function() {
     });
   });
 
+  it('should not prepend needlesly', function() {
+    var schema = string();
+    var spy = sinon.spy(schema, 'concat');
+    var a = { schema };
+    var b = { schema };
+    var c = prependDeep(a, b);
+
+    c.schema.should.equal(schema);
+    spy.should.not.have.been.called();
+  });
+
   it('should getIn correctly', async () => {
     var num = number(),
       inst = object().shape({