@leksiq/func-overload v0.0.9
node-func-overload
The package is intended to help to perform functions "overloading" in JavaScript especially when mutual excluding argument sets are used. Also it is applied to options object and so on.
API
M(arg , (condition | type)) - marks variable
arg
as mandatory. Ifcondition
function ortype
string are supplied thearg
is considered as present whencondition(arg)
resolvestrue
ortypeof(arg)
resolvestype
orarg instanceof type
resolvestrue
.C(arg , (condition | type)) - marks variable
arg
as conditional. Ifcondition
function ortype
string are supplied thearg
is considered as present whencondition(arg)
resolvestrue
ortypeof(arg)
resolvestype
orarg instanceof type
resolvestrue
.
X(arg) - marks variable
arg
as absent. Thearg
itself may be used instead ofX(arg)
. It is introduced for more expressiveness.satisfy(array) - tests if the array of arguments meets rules.
Examples
Function arguments
'use strict';
const
{satisfy, M, C, X} = require('@leksiq/func-overload')
;
function test(a, b, c) {
switch(true) {
case satisfy([M(a, 'string'), C(b), X(c)]):
console.log('case 1');
break;
case satisfy([M(a, v => v === 1), C(b), c]):
console.log('case 2');
break;
case satisfy([a, C(b), M(c)]):
console.log('case 3');
break;
default:
throw 'Inconstistent arguments!';
break;
}
}
test(1);
test(1, 2);
test(1, '2');
test('1', 2);
test()
test('1', 2, 3)
test(undefined, undefined, 3)
produces
case 2
case 2
case 2
case 1
exception: 'Inconstistent arguments!'
exception: 'Inconstistent arguments!'
case 3
Options
function test(options) {
let {a, b, c} = options;
//code like in preceding example
...
}
Some "real life" example
Suppose, we need an Angle class with possible constructors:
let a1 = new Angle(angle); // angle relative to origin
let a2 = new Angle(angle0, angle1); // angle0, angle1 - angles of a2's sides relative to origin
let a3 = new Angle({to: to}); // to - the point {X:..., Y:...} at the plain; a3 - the azimuth from origin to to
let a4 = new Angle({from: from, to: to}); // from, to - the points {X:..., Y:...} at the plain; a4 - the azimuth from from to to
let a5 = new Angle({angle: angle}); // angle relative to origin
let a6 = new Angle({deg: deg}); // angle in degree relative to origin
let a7 = new Angle({angle0: angle0, angle1: angle1}); // angle0, angle1 - angles of a2's sides relative to origin
let a8 = new Angle({deg0: deg0, deg1: deg1}); // deg0, deg1 - angles in degree of a2's sides relative to origin
let a9 = new Angle({angle0: angle0, angle: angle}); // angle0 - angle of a2's first side relative to origin; angle - angle between sides
let a10 = new Angle({angle1: angle1, angle: angle}); // angle1 - angle of a2's second side relative to origin; angle - angle between sides
let a11 = new Angle({deg0: deg0, deg: deg}); // deg0 - angle of a2's first side relative to origin; deg - angle between sides - both in degree
let a12 = new Angle({deg1: deg1, deg: deg}); // deg1 - angle of a2's second side relative to origin; deg - angle between sides - both in degree
In this case we would use the following code:
class Angle {
constructor({from , to, to0, to1, angle, deg, angle0, angle1, deg0, deg1} = {}) {
switch(true) {
case satisfy([from, to, to0, to1, angle, deg, angle0, angle1, deg0, deg1]):
if(arguments.length === 2) {
angle0 = arguments[0];
angle1 = arguments[1];
angle = angle1 - angle0;
} else if(arguments.length === 1) {
angle0 = 0;
angle1 = arguments[0];
angle = angle1 - angle0;
}
break;
case satisfy([C(from, Point.is), M(to, Point.is), to0, to1, angle, deg, angle0, angle1, deg0, deg1]):
angle0 = 0;
angle1 = calc_azimuth(from, to);
angle = angle1;
break;
case satisfy([C(from, Point.is), to, M(to0, Point.is), M(to1, Point.is), angle, deg, angle0, angle1, deg0, deg1]):
angle0 = calc_azimuth(from, to0);
angle1 = calc_azimuth(from, to1);
angle = angle1 - angle0;
break;
case satisfy([from, to, to0, to1, M(angle), deg, angle0, angle1, deg0, deg1]):
angle0 = 0;
angle1 = angle;
break;
case satisfy([from, to, to0, to1, angle, M(deg), angle0, angle1, deg0, deg1]):
angle0 = 0;
angle1 = deg * Math.PI / 180;
break;
case satisfy([from, to, to0, to1, angle, deg, M(angle0), M(angle1), deg0, deg1]):
angle = angle1 - angle0;
break;
case satisfy([from, to, to0, to1, angle, deg, angle0, angle1, M(deg0), M(deg1)]):
angle0 = deg0 * Math.PI / 180;
angle1 = deg1 * Math.PI / 180;
angle = angle1 - angle0;
break;
case satisfy([from, to, to0, to1, M(angle), deg, M(angle0), angle1, deg0, deg1]):
angle1 = angle0 + angle;
break;
case satisfy([from, to, to0, to1, M(angle), deg, angle0, M(angle1), deg0, deg1]):
angle0 = angle1 - angle;
break;
case satisfy([from, to, to0, to1, angle, M(deg), angle0, angle1, M(deg0), deg1]):
angle0 = deg0 * Math.PI / 180;
angle1 = (deg0 + deg) * Math.PI / 180;
break;
case satisfy([from, to, to0, to1, angle, M(deg), angle0, angle1, deg0, M(deg1)]):
angle1 = deg1 * Math.PI / 180;
angle0 = (deg1 - deg) * Math.PI / 180;
break;
default:
throw `Inconsistent arguments: ${arguments}`;
break;
}
// code independent of arguments
...
}
Express.js example
How to select only valid queries...
app.get('/', (request, response) => {
let
{group, symbols, from, to, count, year, ...rest} = request.query,
filename = null
;
if(Object.keys(rest).length) {
response.status(501).end('ERROR unknown parameter(s): ' + JSON.stringify(rest) + '!');
return;
}
switch(true) {
case satisfy([M(group), C(symbols), M(from), M(to), count, year]):
filename = group + '_' + from + '_' + to;
break;
case satisfy([M(group), C(symbols), M(from), to, M(count, v => v > 0), year]):
filename = group + '_' + from + '_' + count;
break;
case satisfy([M(group), C(symbols), from, M(to), M(count, v => v > 0), year]):
filename = group + '_' + count + '_' + to;
break;
case satisfy([M(group), C(symbols), from, to, count, M(year)]):
from = year + '-01-01';
to = year + '-12-31';
filename = group + '_' + year;
break;
default:
response.status(501).end('ERROR bad parameter(s)!');
return;
}
...
}