This code provides a collection of functions (typeErrorTemplate
, standardCompare
, arrayCompare
, objectCompare
, and interface
) for comparing and validating values against expected structures, with the ability to handle type mismatches and loose comparisons.
The code includes several functions for comparing and validating values, including standardCompare
, arrayCompare
, and objectCompare
, which compare arrays and objects against expected structures. The typeErrorTemplate
function is used to create custom error messages for type mismatches, and the interface
function provides a general-purpose comparison function.
npm run import -- "enforcing an interface"
// newer ES6 syntax
function typeErrorTemplate(e, k, t, i, p) {
if(k) {
k = ' ' + k;
}
if(p) {
p = ' of type ' + p;
}
throw new Error(`type mis-match${k || ''}: "${t}" is not "${i}"${p || ''}`, e)
}
function standardCompare(type, expected) {
if(type === expected) return true;
if(!type || !expected || !(expected.isPrototypeOf(type))) {
return false
}
return true;
}
function arrayCompare(compare, specification, loosey) {
var match = specification.map(i => {
try {
return interface(compare, i, loosey);
} catch (e) {
return e;
}
}).filter(s => !s || s.constructor !== Error);
if(match.length > 0) {
return match[0];
}
typeErrorTemplate(void 0,
void 0,
typeof compare,
specification.map(s => typeof s));
}
function objectCompare(compare, specification, loosey) {
var match = Object.keys(specification).reduce((map, k) => {
try {
var m = interface(compare[k], specification[k], loosey);
if(k === 'kernel_config') {
}
if(typeof m !== 'undefined') {
map[k] = m;
}
} catch (e) {
typeErrorTemplate(e,
k,
typeof compare[k],
specification[k],
specification[k].constructor)
}
return map;
}, {});
return match;
}
// loosey means no exceptions are thrown and undefined type is assumed
function interface(compare, specification, loosey) {
var type = compare === void 0 || compare === null
? compare
: Object.getPrototypeOf(compare);
var expected = specification === void 0 || specification === null
? specification
: Object.getPrototypeOf(specification);
switch(expected) {
case Array.prototype:
// if it is an empty array to the actual compare on the object,
// if it is not an empty array compare to each type in the array
if(specification.length > 0) {
return arrayCompare(compare, specification, loosey)
}
case Object.prototype:
// compare properties on object
if(Object.keys(specification).length > 0
&& (typeof compare === 'object' || typeof compare === 'function')) {
return objectCompare(compare, specification, loosey)
}
default:
if(standardCompare(type, expected)
// if loosey is not explicitly set to false,
// return void 0 (undefined) instead of error
// only if compare it undefined,
// still error on other wrong types
|| (loosey !== false && compare === void 0)) {
return compare;
}
typeErrorTemplate(void 0,
void 0,
typeof compare,
typeof specification,
expected)
}
}
module.exports = interface;
if(typeof $ !== 'undefined') {
var jsonInterface = {
display_name: '',
argv: [], // not optional
language: '', // not optional
metadata: [void 0, {}], // optional
env: [void 0, {}], // TODO dictionary descriptor types?
interrupt_mode: [void 0, '']
}
console.log(interface({
display_name: 'Node JS',
argv: [],
language: '',
some_other_stuff: true
}, jsonInterface))
// expected output {"display_name":"Node JS","argv":[],"language":""}
}
/**
* Type error template function.
* Throws an error if the type of a variable does not match the expected type.
*
* @param {Error} e The error to throw.
* @param {string} k The key or name of the variable (optional).
* @param {string} t The type of the variable.
* @param {string} i The expected type.
* @param {string} p The type of the expected type (optional).
*/
function typeErrorTemplate(e, k, t, i, p) {
if (k) {
k = ` ${k}`;
}
if (p) {
p = ` of type ${p}`;
}
throw new Error(`Type mis-match${k || ''}: "${t}" is not "${i}"${p || ''}`, e);
}
/**
* Checks if two types are equal or if the expected type is a parent of the actual type.
*
* @param {*} type The actual type.
* @param {*} expected The expected type.
* @returns {boolean} True if the types are equal or if the expected type is a parent of the actual type, false otherwise.
*/
function standardCompare(type, expected) {
return type === expected || expected.isPrototypeOf(type);
}
/**
* Compares an array to an array specification.
* Throws an error if the array does not match the specification.
*
* @param {*} compare The array to compare.
* @param {array} specification The array specification.
* @param {boolean} loosey Whether to throw an error or return undefined if the types do not match.
* @returns {*} The compared array or an error.
*/
function arrayCompare(compare, specification, loosey) {
const match = specification.map((i, index) => {
try {
return interface(compare[index], i, loosey);
} catch (e) {
return e;
}
}).filter(s =>!s ||!(s instanceof Error));
if (match.length > 0) {
return match[0];
}
typeErrorTemplate(new Error('Type mis-match'), void 0, typeof compare, specification.map(s => typeof s));
}
/**
* Compares an object to an object specification.
* Throws an error if the object does not match the specification.
*
* @param {*} compare The object to compare.
* @param {object} specification The object specification.
* @param {boolean} loosey Whether to throw an error or return undefined if the types do not match.
* @returns {*} The compared object or an error.
*/
function objectCompare(compare, specification, loosey) {
const match = Object.keys(specification).reduce((map, key) => {
try {
const m = interface(compare[key], specification[key], loosey);
if (key === 'kernel_config') {
// No-op for kernel_config
}
if (typeof m!== 'undefined') {
map[key] = m;
}
} catch (e) {
typeErrorTemplate(e, key, typeof compare[key], specification[key]);
}
return map;
}, {});
return match;
}
/**
* Compares two values using the interface function.
*
* @param {*} compare The value to compare.
* @param {*} specification The value specification.
* @param {boolean} loosey Whether to throw an error or return undefined if the types do not match.
* @returns {*} The compared value or an error.
*/
function interface(compare, specification, loosey = false) {
let type, expected;
if (compare === void 0 || compare === null) {
type = compare;
} else {
type = Object.getPrototypeOf(compare);
}
if (specification === void 0 || specification === null) {
expected = specification;
} else {
expected = Object.getPrototypeOf(specification);
}
switch (expected) {
case Array.prototype:
if (specification.length > 0) {
return arrayCompare(compare, specification, loosey);
}
case Object.prototype:
if (Object.keys(specification).length > 0 && (typeof compare === 'object' || typeof compare === 'function')) {
return objectCompare(compare, specification, loosey);
}
default:
if (standardCompare(type, expected) || (loosey && compare === void 0)) {
return compare;
}
typeErrorTemplate(new Error('Type mis-match'), void 0, typeof compare, typeof specification, expected);
}
}
module.exports = interface;
if (typeof $!== 'undefined') {
const jsonInterface = {
display_name: '',
argv: [], // not optional
language: '', // not optional
metadata: [void 0, {}], // optional
env: [void 0, {}], // TODO dictionary descriptor types?
interrupt_mode: [void 0, '']
};
console.log(interface({
display_name: 'Node JS',
argv: [],
language: '',
some_other_stuff: true
}, jsonInterface));
}
Code Breakdown
typeErrorTemplate(e, k, t, i, p)
e
: Error code (not used in the function).k
: Additional string to append to the error message.t
: Type of the actual value.i
: Expected type.p
: Type of the expected value.Error
object.standardCompare(type, expected)
type
: Actual value.expected
: Expected value.true
if the values match, false
otherwise.arrayCompare(compare, specification, loosey)
compare
: Array to compare.specification
: Expected array structure.loosey
: Optional parameter (not used in the function).undefined
if the arrays match.objectCompare(compare, specification, loosey)
compare
: Object to compare.specification
: Expected object structure.loosey
: Optional parameter (not used in the function).undefined
if the objects match.interface(compare, specification, loosey)
compare
: Value to compare.specification
: Expected value structure.loosey
: Optional parameter (not used in the function).arrayCompare
and objectCompare
use the interface
function to compare individual properties.typeErrorTemplate
function is used to create custom error messages for type mismatches.loosey
parameter is not used in the functions, indicating that it might be a deprecated or unused feature.