diff --git a/.gitignore b/.gitignore index e61051f..faad3eb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules/ /coverage/ +*.lock diff --git a/package.json b/package.json index dc61dae..dfd6b48 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,8 @@ { "name": "@uci-utils/type", - "version": "0.2.3", - "description": "A variable type check utility - extentsion of TypeChecker package", - "main": "src/type.js", + "version": "0.5.0", + "description": "A variable type check and casting utility - extension of typechecker package plus type casting", + "main": "src/", "scripts": { "test": "./node_modules/.bin/mocha -r esm --timeout 30000", "testd": "UCI_ENV=dev ./node_modules/.bin/nodemon --exec './node_modules/.bin/mocha -r esm --timeout 30000' || exit 0", @@ -19,6 +19,8 @@ "keywords": [ "node.js", "type", + "typecast", + "cast", "typeof", "typechecker", "variables" @@ -28,12 +30,13 @@ }, "homepage": "https://github.com/uCOMmandIt/uci-utils#readme", "dependencies": { - "typechecker": "^5.0.0" + "@uci-utils/to-boolean": "^0.1.5", + "typechecker": "^6.3.0" }, "devDependencies": { "chai": "^4.2.0", "esm": "^3.2.25", - "mocha": "^6.2.2", - "nodemon": "^1.19.4" + "mocha": "^7.1.0", + "nodemon": "^2.0.2" } } diff --git a/readme.md b/readme.md index c37ff89..22287fc 100644 --- a/readme.md +++ b/readme.md @@ -1,3 +1,29 @@ ### uCOMmandIt Type Library -Extension of TypeChecker Package +Extends the type checker Library +All includes some simple type casting including using uci to-boolean for boolean type casting. + +See type checker api http://master.typechecker.bevry.surge.sh/docs/ + +exentions are + +`.isPromise` +`.isBuffer` + +TODO add + +import isObservable from 'is-observable' +import isPromise from 'p-is-promise' + +``` +function isEmitter(emitter) { + if (!emitter) return false + let check = ['on','emit'] + return check.reduce((acc,fn)=> acc && typeof emitter[fn] ==='function',true) +} + +function isAsync(fn) { + if (typeof fn !== 'function') return false + return (isPromise(fn) || fn.constructor.name === 'AsyncFunction') +} +``` diff --git a/src/cast.js b/src/cast.js new file mode 100644 index 0000000..d87a747 --- /dev/null +++ b/src/cast.js @@ -0,0 +1,93 @@ +import { toBoolean } from '@uci-utils/to-boolean' +// adapted from "https://github.com/eivindfjeldstad/const git" + +/** + * Cast `val` to `String` + * + * @param {Mixed} val + * @api public + */ + +const toString = function (val) { + if (null == val) return '' + return val.toString() +} + +/** + * Cast `val` to `Number` + * + * @param {Mixed} val + * @api public + */ + +const toNumber = function (val) { + var num = parseFloat(val) + return isNaN(num) + ? 0 + : num +} + +/** + * Cast `val` to a`Date` + * + * @param {Mixed} val + * @api public + */ + +const toDate = function () { + var date = new Date(...arguments) + return isNaN(date.getTime()) + ? new Date(0) + : date +} + +/** + * Cast `val` to `Array` + * + * @param {Mixed} val + * @api public + */ + +const toArray = function (val) { + if (val == null) return [] + if (val instanceof Array) return val + if (typeof val != 'string') return [val] + + var arr = val.split(',') + for (var i = 0; i < arr.length; i++) { + arr[i] = arr[i].trim() + } + + return arr +} + +/** + * Cast given `val` to `type` + * + * @param {Mixed} val + * @param {String} type + * @api public + */ + +const typecast={string:toString,number:toNumber,date:toDate,boolean:toBoolean,array:toArray} + +function cast (val, type) { + var fn = typecast[type] + if (typeof fn != 'function') throw new Error('cannot cast to ' + type) + return fn(val) +} + +export { cast, typecast, toBoolean, toArray, toDate, toNumber, toString } + + +// TODO support registering alternative casting functions with a register function +/** + * Cast `val` to `Boolean` + * + * @param {Mixed} val + * @api public + */ + +// const toBoolean = function (val) { +// return !! val && val !== 'false' && val !== '0' +// } diff --git a/src/type.js b/src/check.js similarity index 90% rename from src/type.js rename to src/check.js index 3429592..51d4e37 100644 --- a/src/type.js +++ b/src/check.js @@ -1,5 +1,7 @@ import * as typechecker from 'typechecker' +// TODO add observable types and other custom uci types + const customTypeChecker = Object.assign({}, typechecker) // typechecker is frozen // add custom types @@ -24,4 +26,4 @@ customTypeChecker.getType = function customGetType(value, _typeMap) { } export default customTypeChecker -export { customTypeChecker as type } +export { customTypeChecker as check } diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..189a00e --- /dev/null +++ b/src/index.js @@ -0,0 +1,6 @@ +// import { cast, typecast, } from './cast' +import check from './check' + +export * from './cast' +export { check } from './check' +export default check // default is just the typechecker only diff --git a/test/cast.test.js b/test/cast.test.js new file mode 100644 index 0000000..8d42874 --- /dev/null +++ b/test/cast.test.js @@ -0,0 +1,119 @@ +import assert from 'assert' +import { check, typecast, cast, toString, toBoolean } from '../src' + +console.log(typecast.boolean) +console.log(toBoolean) + +describe('.string()', function () { + it('should return a string', function () { + assert(typecast.string(2) === '2') + assert(check.isString(typecast.string(2))) + }) + + it('should return an empty string when given a null-ish value', function () { + assert(typecast.string(null) === '') + assert(typecast.string(undefined) === '') + }) + + it('should support toString', function () { + assert(toString(2) === '2') + assert(check.isString(toString(2))) + }) + +}) + + + +describe('.number()', function () { + it('should return a number', function () { + assert(typecast.number('123') === 123) + }) + + it('should return 0 if typecasting fails', function () { + assert(typecast.number('abc') === 0) + }) + + it('should return an 0 when given a null-ish value', function () { + assert(typecast.number(null) === 0) + assert(typecast.number(undefined) === 0) + }) +}) + +const adate = [1995, 11, 17, 3, 24, 0] +const sdate = 'December 17, 1995 03:24:00' + +describe('.date()', function () { + it('should return a date from a string', function () { + assert(typecast.date(sdate).getTime() === 819199440000) + }) + + it('should return a date from an array of values', function () { + assert(typecast.date(...adate).getTime() === 819199440000) + }) + + it('should return default date if typecasting fails', function () { + assert(typecast.date('abc') instanceof Date) + assert(typecast.date('abc').valueOf() == 0) + }) + + it('should return default date when given a null-ish value', function () { + assert(typecast.date(null) instanceof Date) + assert(typecast.date(null).valueOf() == 0) + + assert(typecast.date(undefined) instanceof Date) + assert(typecast.date(undefined).valueOf() == 0) + }) +}) + +describe('.array()', function () { + it('should return an array', function () { + var arr = [1, 2, 3] + assert(typecast.array(arr) === arr) + assert(typecast.array(1) instanceof Array) + assert(typecast.array('a, b, c').length == 3) + assert(typecast.array('a, b, c')[1] === 'b') + }) + + it('should preserve non-string objects', function () { + var now = new Date() + assert(Array.isArray(typecast.array(now))) + assert(typecast.array(now).length == 1) + assert(typecast.array(now)[0] === now) + }) + + it('should return an empty array when given a null-ish value', function () { + assert(Array.isArray(typecast.array(null))) + assert(typecast.array(null).length == 0) + + assert(Array.isArray(typecast.array(undefined))) + assert(typecast.array(undefined).length == 0) + }) +}) + +describe('.boolean()', function () { + it('should be using UCI to-boolean', function () { + assert(typecast.boolean('true') === true) + assert(typecast.boolean('nope') === false) + assert(typecast.boolean('sure') === true) + assert(typecast.boolean('bogus') === false) + assert(typecast.boolean() === false) // undefined is false + assert(typecast.boolean(-1) === false) + assert(typecast.boolean(4) === true) + }) +}) + +describe('generic cast function', function () { + it('should work', function () { + assert(cast(123, 'string') === '123') + }) + + it('should throw when given an invalid type', function () { + var err + try { + typecast(1, 'invalid') + } catch (e) { + err = e + } + assert(err) + }) +}) diff --git a/test/type.test.js b/test/check.test.js similarity index 81% rename from test/type.test.js rename to test/check.test.js index 0d81533..078398c 100644 --- a/test/type.test.js +++ b/test/check.test.js @@ -1,7 +1,7 @@ import { expect } from 'chai' -import u from '../src/type' +import u from '../src' -describe('Variable Types Library - ', function () { +describe('Type Check Library - ', function () { it('Should include custom types', function () { expect(u.isBuffer(Buffer.from('this is a test'))).to.equal(true) @@ -10,7 +10,7 @@ describe('Variable Types Library - ', function () { expect(u.getType(Promise.resolve(2))).to.equal('promise') }) - it('Should load typechecker', function () { + it('Should include typechecker methods', function () { expect(u.isPlainObject([1])).to.equal(false) expect(u.getType(true)).to.equal('boolean') expect(u.getObjectType('a string')).to.equal('[object String]')