Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 53 additions & 17 deletions __tests__/castable.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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));
Expand All @@ -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
}`;
Expand All @@ -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"
}
Expand Down Expand Up @@ -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));
Expand All @@ -168,3 +168,39 @@ 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);
}
});

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);
}
});
22 changes: 21 additions & 1 deletion lib/castable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class Castable {
case 'String':
return String(source);
case 'Boolean':
return source.toString() === 'true';
return toBool(source);
case 'Array':
const elementType = Reflect.getMetadata('custom:element-type' + depth, this, propertyKey) as Function;
const nextDepth = depth + 1;
Expand All @@ -48,3 +48,23 @@ export class Castable {
}
}
}

/**
* Converts a value to a boolean.
* @param val value to be converted
*/
export function toBool(val: any): boolean {
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;
}
}