avocado-type-checker
Version:
A javascript type checker. Also a tasty fruit with megafauna dispersal syndrome, leading to an evolutionary anachronism.
150 lines (131 loc) • 3.7 kB
JavaScript
import { default as findByType } from './findByType';
var types = [];
// av's main function is a getter-setter-combo
// it expects a default value and a validation function
// the validation function should return the validated value or throw an exception
// it shouldn't return 'false' for non-validating values
var av = function (i, validate){
// we offer a shortcut to get types when only one argument is provided
if (arguments.length === 1) {
return av.type(i);
}
// we store the value in private scope
var _i;
// our getter-setter-combo including validation
var me = function (d){
if (!arguments.length) {
if (typeof _i === 'object'){
var o = {};
for (var prop in _i){
o[prop] = _i[prop]();
}
return o;
} else {
return _i;
}
}
_i = validate(d);
// if _i is an object we expose the getter/setter methods of its attributes
if (typeof _i === 'object'){
for (var prop_object in _i){
me[prop_object] = _i[prop_object];
}
}
};
// we initialize the getter-setter-combo with the provided value
me(i);
// return the getter-setter-combo (allows chaining, among other things)
return me;
};
av.type = function() {
var typeIndex;
var typeName;
var typeData;
if (arguments.length === 0) {
throw new Error('av.type requires at least one argument.');
}
typeName = arguments[0];
typeIndex = findByType(types, typeName);
// get a type by name
if (arguments.length === 1) {
if (typeIndex === -1) {
throw new Error('type not found');
}
return types[typeIndex].type;
// set a type by name
} else if (arguments.length === 2) {
typeData = arguments[1];
if (typeIndex !== -1) {
throw new Error('type specification already exists.');
}
if (typeof typeData === 'function') {
types.push({
typeName: typeName,
type: function(i) {
return av(i, typeData);
}
});
} else if (typeof typeData === 'object') {
types.push({
typeName: typeName,
type: function(i) {
return av(i, function(d) {
if (typeof d === 'object'){
var _i = {};
for (var prop in typeData){
_i[prop] = av.type(typeData[prop])(d[prop]);
}
return _i;
} else throw 'not a valid object';
});
}
});
} else {
throw new Error('argument 2 typeData must be either a function or object.');
}
return av;
}
throw new Error('invalid amount of arguments for av.type()');
};
// isValid allows you to test if a value (v) is a valid type (t)
av.isValid = function (t, v){
try {
t(v);
return true;
} catch (err) {
return false;
}
};
// a boolean type and its validation function
av.type('boolean', function (d){
if (typeof d === 'boolean') {
return d;
} else throw new Error('d is not boolean');
});
// http://stackoverflow.com/a/20779354/2266116
var isInteger = function (nVal){
return typeof nVal === 'number' && isFinite(nVal) && nVal > -9007199254740992 && nVal < 9007199254740992 && Math.floor(nVal) === nVal;
};
av.type('int', function(d) {
if(!isInteger(d)) {
throw new Error('d is not an integer');
}
return d;
});
av.type('float', function(d){
if(typeof d !== 'number') {
throw 'd is not a number';
}
return d;
});
av.type('string', function (d){
if (typeof d === 'string') {
return d;
} else throw 'd is not a string';
});
av.type('collection', function (d){
if (d && d.constructor === Array) {
return d;
} else throw 'not an array';
});
export default av;