From 36fa5202f3ed94b1ec52a40edf15f41d4a948e3e Mon Sep 17 00:00:00 2001 From: Jarom Loveridge Date: Thu, 12 Apr 2018 11:54:51 -0600 Subject: [PATCH 1/3] Support expanded support for converting to boolean values. --- __tests__/castable.ts | 59 +++++++++++++++++++++++++++++++------------ lib/castable.ts | 2 +- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/__tests__/castable.ts b/__tests__/castable.ts index f6e4f12..32f5804 100644 --- a/__tests__/castable.ts +++ b/__tests__/castable.ts @@ -10,10 +10,10 @@ class Product extends Castable { test('Convert a simple object', () => { const serverResponse = `{ - "name": "Milk", - "price": "200", - "tax": "10", - "date": "2017-10-20T06:28:08Z", + "name": "Milk", + "price": "200", + "tax": "10", + "date": "2017-10-20T06:28:08Z", "onSale": "false" }`; const product = new Product(JSON.parse(serverResponse)); @@ -25,10 +25,10 @@ test('Convert a simple object', () => { test('Convert a simple object with unknown field', () => { const serverResponse = `{ - "name": "Milk", - "price": "200", - "tax": "10", - "date": "2017-10-20T06:28:08Z", + "name": "Milk", + "price": "200", + "tax": "10", + "date": "2017-10-20T06:28:08Z", "onSale": "false", "unknown": 10 }`; @@ -43,16 +43,16 @@ test('Convert a simple object with unknown field', () => { test('Convert an object array', () => { const serverResponse = `[ { - "name": "Milk", - "price": "200", - "tax": "10", + "name": "Milk", + "price": "200", + "tax": "10", "date": "2017-10-20T06:28:08Z", "onSale": "true" }, { - "name": "Water", - "price": "50", - "tax": "5", + "name": "Water", + "price": "50", + "tax": "5", "date": "2017-10-25T06:28:08Z", "onSale": "false" } @@ -150,10 +150,10 @@ test('Convert 2D object array', () => { @element(Array, Pair) arr: Pair[][]; } - const s = `{ + const s = `{ "arr": [ [ { "name": "abc", "n": "123" }, { "name": "def", "n": "999" } ], - [ { "name": "abc2", "n": "200" }, { "name": "def2", "n": "300" } ] + [ { "name": "abc2", "n": "200" }, { "name": "def2", "n": "300" } ] ] }`; const c = new C(JSON.parse(s)); @@ -168,3 +168,30 @@ test('Convert 2D object array', () => { expect(typeof c.arr[1][0].n).toBe('number'); expect(c.arr[1][0].n).toBe(200); }); + +test('Convert expanded boolean values', () => { + class C extends Castable { + @cast y: boolean; + @cast yes: boolean; + @cast intPos: boolean; + @cast intNeg: boolean; + @cast t: boolean; + @cast n: boolean; + @cast no: boolean; + @cast zero: boolean; + @cast f: boolean; + @cast s: boolean; + @cast null: boolean; + } + const s = `{ + "y": "y", "yes": "yes", "t": "t", "intNeg": -1, "intPos": 1, + "n": "n", "no": "no", "zero": 0, "f": "f", "s": "arbitrary string", "null": null + }`; + const c = new C(JSON.parse(s)); + for (const key of ['y', 'yes', 't', 'intNeg', 'intPos']) { + expect(c[key]).toBe(true); + } + for (const key of ['n', 'no', 'zero', 'f']) { + expect(c[key]).toBe(false); + } +}); diff --git a/lib/castable.ts b/lib/castable.ts index 3a7ace5..de5ac91 100644 --- a/lib/castable.ts +++ b/lib/castable.ts @@ -38,7 +38,7 @@ export class Castable { case 'String': return String(source); case 'Boolean': - return source.toString() === 'true'; + return typeof source === 'number' ? source !== 0 : /^(t|true|y|yes)/.test(String(source)); case 'Array': const elementType = Reflect.getMetadata('custom:element-type' + depth, this, propertyKey) as Function; const nextDepth = depth + 1; From da1367ba6b16c9025a70c373f569fc861e22d3f6 Mon Sep 17 00:00:00 2001 From: Jarom Loveridge Date: Thu, 12 Apr 2018 13:21:19 -0600 Subject: [PATCH 2/3] Expose `toBool` as a utility method. --- __tests__/castable.ts | 11 ++++++++++- lib/castable.ts | 10 +++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/__tests__/castable.ts b/__tests__/castable.ts index 32f5804..116ba1b 100644 --- a/__tests__/castable.ts +++ b/__tests__/castable.ts @@ -1,4 +1,4 @@ -import { cast, element, Castable } from '../lib/castable'; +import { cast, element, Castable, toBool } from '../lib/castable'; class Product extends Castable { @cast name: string; @@ -195,3 +195,12 @@ test('Convert expanded boolean values', () => { expect(c[key]).toBe(false); } }); + +test('should be able to use `toBool` directly', () => { + for (const val of ['y', 'yes', 't', -1, 1]) { + expect(toBool(val)).toBe(true); + } + for (const val of ['n', 'no', 0, 'f', null, void 0]) { + expect(toBool(val)).toBe(false); + } +}); diff --git a/lib/castable.ts b/lib/castable.ts index de5ac91..68cb30b 100644 --- a/lib/castable.ts +++ b/lib/castable.ts @@ -38,7 +38,7 @@ export class Castable { case 'String': return String(source); case 'Boolean': - return typeof source === 'number' ? source !== 0 : /^(t|true|y|yes)/.test(String(source)); + return toBool(source); case 'Array': const elementType = Reflect.getMetadata('custom:element-type' + depth, this, propertyKey) as Function; const nextDepth = depth + 1; @@ -48,3 +48,11 @@ export class Castable { } } } + +/** + * Converts various values to a boolean. + * @param val value to be converted + */ +export function toBool(val: any): boolean { + return typeof val === 'number' ? val !== 0 : /^(t|true|y|yes)/.test(String(val)) +} \ No newline at end of file From e0cc45dcd31e49a670d6004e3b4b1b0dad1f627e Mon Sep 17 00:00:00 2001 From: Jarom Loveridge Date: Sat, 14 Apr 2018 14:52:21 -0600 Subject: [PATCH 3/3] Make boolean conversion cleaner. --- lib/castable.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/castable.ts b/lib/castable.ts index 68cb30b..3b90798 100644 --- a/lib/castable.ts +++ b/lib/castable.ts @@ -50,9 +50,21 @@ export class Castable { } /** - * Converts various values to a boolean. + * Converts a value to a boolean. * @param val value to be converted */ export function toBool(val: any): boolean { - return typeof val === 'number' ? val !== 0 : /^(t|true|y|yes)/.test(String(val)) -} \ No newline at end of file + switch (typeof val) { + case 'boolean': + return val; + case 'number': + // NaN and 0 should both be false, anything else true + return !!val; + case 'object': + return val ? Object.keys(val).length > 0 : false; + case 'string': + return /^(t|true|y|yes|on|1)$/i.test(val.trim()); + default: + return false; + } +}