diff --git a/README.md b/README.md index 47f5d47d8..5973acd36 100644 --- a/README.md +++ b/README.md @@ -1152,6 +1152,36 @@ object({ }); ``` +#### `object.pick(keys: string[]): Schema` + +Create a new schema from a subset of the original's fields. + +```js +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.default(); // => { age: 30, name: 'pat'} +``` + +#### `object.omit(keys: string[]): Schema` + +Create a new schema with fields omitted. + +```js +const person = object({ + age: number().default(30).required(), + name: string().default('pat').required(), + color: string().default('red').required(), +}); + +const nameAndAge = person.omit('color']); +nameAndAge.default(); // => { age: 30, name: 'pat'} +``` + #### `object.from(fromKey: string, toKey: string, alias: boolean = false): Schema` Transforms the specified key to a new key. If `alias` is `true` then the old key will be left. diff --git a/src/object.js b/src/object.js index cbb51629b..30080cdd2 100644 --- a/src/object.js +++ b/src/object.js @@ -261,6 +261,29 @@ inherits(ObjectSchema, MixedSchema, { return next; }, + pick(keys) { + const picked = {}; + for (const key of keys) { + if (this.fields[key]) picked[key] = this.fields[key]; + } + + return this.clone().withMutation((next) => { + next.fields = {}; + return next.shape(picked); + }); + }, + + omit(keys) { + const next = this.clone(); + const fields = next.fields; + next.fields = {}; + for (const key of keys) { + delete fields[key]; + } + + return next.withMutation((next) => next.shape(fields)); + }, + from(from, to, alias) { let fromGetter = getter(from, true); diff --git a/test/object.js b/test/object.js index 18cf67378..2feefb8dc 100644 --- a/test/object.js +++ b/test/object.js @@ -468,7 +468,7 @@ describe('Object types', () => { let inst = lazy((_, options) => { options.should.equal(opts); done(); - return string(); + return object(); }); inst.cast({ nested: 'foo' }, opts); @@ -863,15 +863,6 @@ describe('Object types', () => { expect(inst.nullable().cast(null)).to.equal(null); }); - // it('should camelCase with leading underscore', () => { - // let inst = object().camelCase() - // - // inst - // .cast({ CON_STAT: 5, __isNew: true, __IS_FUN: true }) - // .should - // .eql({ conStat: 5, __isNew: true, __isFun: true }) - // }) - it('should CONSTANT_CASE keys', () => { let inst = object() .shape({ @@ -888,6 +879,42 @@ describe('Object types', () => { expect(inst.nullable().cast(null)).to.equal(null); }); + it('should pick', async () => { + let inst = object({ + age: number().default(30).required(), + name: string().default('pat').required(), + color: string().default('red').required(), + }); + + expect(inst.pick(['age', 'name']).default()).to.eql({ + age: 30, + name: 'pat', + }); + + expect( + await inst.pick(['age', 'name']).validate({ age: 24, name: 'Bill' }), + ).to.eql({ + age: 24, + name: 'Bill', + }); + }); + + it('should omit', async () => { + let inst = object({ + age: number().default(30).required(), + name: string().default('pat').required(), + color: string().default('red').required(), + }); + + expect(inst.omit(['age', 'name']).default()).to.eql({ + color: 'red', + }); + + expect( + await inst.omit(['age', 'name']).validate({ color: 'mauve' }), + ).to.eql({ color: 'mauve' }); + }); + xit('should handle invalid shapes better', async () => { var schema = object().shape({ permissions: undefined,