From 0524629cb766eb72b0d18a13c3512c860a3b5ac6 Mon Sep 17 00:00:00 2001 From: Christopher Pardy Date: Thu, 26 Jul 2018 15:30:34 -0400 Subject: [PATCH] Treat missing fields as undefined \n* Add test to ensure a missing required field is an error* Add test to ensure undefined is allowed for undefined fields* Add test to ensure that missing fields are treated as undefined. --- src/index.spec.ts | 17 ++++++++++++++++- src/types/record.ts | 12 +++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/index.spec.ts b/src/index.spec.ts index f3172e10..5df9d4c8 100644 --- a/src/index.spec.ts +++ b/src/index.spec.ts @@ -87,6 +87,7 @@ const runtypes = { InstanceOfSomeClass: InstanceOf(SomeClass), InstanceOfSomeOtherClass: InstanceOf(SomeOtherClass), DictionaryOfArraysOfSomeClass: Dictionary(Array(InstanceOf(SomeClass))), + OptionalKey: Record({ foo: String, bar: Union(Number, Undefined) }), }; type RuntypeName = keyof typeof runtypes; @@ -111,7 +112,7 @@ const testValues: { value: always; passes: RuntypeName[] }[] = [ { value: { Boolean: true, Number: 3 }, passes: ['record1', 'union1', 'Partial'] }, { value: { Boolean: true }, passes: ['Partial'] }, { value: { Boolean: true, foo: undefined }, passes: ['Partial'] }, - { value: { Boolean: true, foo: 'hello' }, passes: ['Partial'] }, + { value: { Boolean: true, foo: 'hello' }, passes: ['Partial', 'OptionalKey'] }, { value: { Boolean: true, foo: 5 }, passes: [] }, { value: (x: number, y: string) => x + y.length, passes: ['Function'] }, { value: { name: undefined, likes: [] }, passes: [] }, @@ -130,6 +131,8 @@ const testValues: { value: always; passes: RuntypeName[] }[] = [ { value: [1, 2, 3, 4], passes: ['ArrayNumber', 'CustomArray', 'CustomArrayWithMessage'] }, { value: new SomeClass(42), passes: ['InstanceOfSomeClass'] }, { value: { xxx: [new SomeClass(55)] }, passes: ['DictionaryOfArraysOfSomeClass'] }, + { value: { foo: 'hello' }, passes: ['OptionalKey', 'Dictionary'] }, + { value: { foo: 'hello', bar: undefined }, passes: ['OptionalKey'] }, ]; for (const { value, passes } of testValues) { @@ -297,6 +300,18 @@ describe('check errors', () => { ); }); + it('record missing keys', () => { + assertThrows( + { name: 'Jack' }, + Record({ + name: String, + age: Number, + }), + 'Expected number, but was undefined', + 'age', + ); + }); + it('record complex', () => { assertThrows( { name: 'Jack', age: 10, likes: [{ title: false }] }, diff --git a/src/types/record.ts b/src/types/record.ts index 463a9f2e..bab07fca 100644 --- a/src/types/record.ts +++ b/src/types/record.ts @@ -21,13 +21,11 @@ export function Record(fields: O) { // tslint:disable-next-line:forin for (const key in fields) { - if (hasKey(key, x)) { - try { - fields[key].check(x[key]); - } catch ({ key: nestedKey, message }) { - throw validationError(message, nestedKey ? `${key}.${nestedKey}` : key); - } - } else throw validationError(`Expected ${key} to be ${show(fields[key].reflect)}`, key); + try { + fields[key].check(hasKey(key, x) ? x[key] : undefined); + } catch ({ key: nestedKey, message }) { + throw validationError(message, nestedKey ? `${key}.${nestedKey}` : key); + } } return x as O;