typed-function
Advanced tools
Comparing version 0.6.0 to 0.6.1
@@ -5,7 +5,7 @@ var typed = require('../typed-function'); | ||
var fn1 = typed('number', function (a) { | ||
return a * a; | ||
return a + a; | ||
}); | ||
var fn2 = typed('string', function (a) { | ||
var value = +a; | ||
return value * value; | ||
return value + value; | ||
}); | ||
@@ -18,2 +18,2 @@ | ||
console.log(fn3(2)); // outputs 4 | ||
console.log(fn3('3')); // outputs 9 | ||
console.log(fn3('3')); // outputs 6 |
# History | ||
## 2015-02-07, version 0.6.1 | ||
- Large code refactoring. | ||
- Fixed bugs related to any type parameters. | ||
## 2015-01-16, version 0.6.0 | ||
- Removed the configuration function `minify` (it's not clear yet whether | ||
minifying really improves the performance). | ||
- Removed the configuration option `minify` | ||
(it's not clear yet whether minifying really improves the performance). | ||
- Internal code simplifications. | ||
@@ -8,0 +14,0 @@ - Bug fixes. |
{ | ||
"name": "typed-function", | ||
"version": "0.6.0", | ||
"version": "0.6.1", | ||
"description": "Type checking for JavaScript functions", | ||
@@ -5,0 +5,0 @@ "author": "Jos de Jong <wjosdejong@gmail.com> (https://github.com/josdejong)", |
@@ -8,3 +8,3 @@ /** | ||
*/ | ||
(function (root, factory) { | ||
(function (factory) { | ||
if (typeof define === 'function' && define.amd) { | ||
@@ -14,51 +14,14 @@ // AMD. Register as an anonymous module. | ||
} else if (typeof exports === 'object') { | ||
// Node. Does not work with strict CommonJS, but | ||
// OldNode. Does not work with strict CommonJS, but | ||
// only CommonJS-like environments that support module.exports, | ||
// like Node. | ||
// like OldNode. | ||
module.exports = factory(); | ||
} else { | ||
// Browser globals (root is window) | ||
root.typed = factory(); | ||
window.typed = factory(); | ||
} | ||
}(this, function () { | ||
}(function () { | ||
'use strict'; | ||
/** | ||
* Order Params | ||
* any type ('any') will be ordered last, and object as second last (as other types | ||
* may be an object as well, like Array) | ||
* @param {Param} a | ||
* @param {Param} b | ||
* @returns {number} Returns 1 if a > b, -1 if a < b, and else 0. | ||
*/ | ||
function compareParams(a, b) { | ||
if (a.anyType) return 1; | ||
if (a.anyType) return -1; | ||
if (a.types.indexOf('Object') !== -1) return 1; | ||
if (b.types.indexOf('Object') !== -1) return -1; | ||
return 0; | ||
} | ||
/** | ||
* Merge multiple objects. | ||
* Expects one or more Objects as input arguments | ||
*/ | ||
function merge () { | ||
var obj = {}; | ||
for (var i = 0; i < arguments.length; i++) { | ||
var o = arguments[i]; | ||
for (var prop in o) { | ||
if (o.hasOwnProperty(prop)) { | ||
obj[prop] = o[prop]; | ||
} | ||
} | ||
} | ||
return obj; | ||
} | ||
/** | ||
* Get a type test function for a specific data type | ||
@@ -87,2 +50,55 @@ * @param {string} type A data type like 'number' or 'string' | ||
/** | ||
* Create an ArgumentsError. Creates messages like: | ||
* | ||
* Unexpected type of argument (expected: ..., actual: ..., index: ...) | ||
* Too few arguments (expected: ..., index: ...) | ||
* Too many arguments (expected: ..., actual: ...) | ||
* | ||
* @param {String} fn Function name | ||
* @param {Object} args The actual arguments | ||
* @param {Number} index Current argument index | ||
* @param {string} [expected] An optional, comma separated string with | ||
* expected types on given index | ||
* @extends Error | ||
*/ | ||
function createError(fn, args, index, expected) { | ||
var actual = args[index]; | ||
var actualType = getTypeOf(actual); | ||
var _expected = expected ? expected.split(',') : null; | ||
var anyType = _expected && contains(_expected, 'any'); | ||
var message; | ||
var data = { | ||
fn: fn, | ||
index: index, | ||
actual: actual, | ||
expected: _expected | ||
}; | ||
//console.log('createError', fn, args, index, expected) // TODO: cleanup | ||
// TODO: add function name to the message | ||
if (_expected) { | ||
if (args.length > index && !anyType) { | ||
// unexpected type | ||
message = 'Unexpected type of argument' + | ||
' (expected: ' + _expected.join(' or ') + ', actual: ' + actualType + ', index: ' + index + ')'; | ||
} | ||
else { | ||
// too few arguments | ||
message = 'Too few arguments' + | ||
' (expected: ' + _expected.join(' or ') + ', index: ' + index + ')'; | ||
} | ||
} | ||
else { | ||
// too many arguments | ||
message = 'Too many arguments' + | ||
' (expected: ' + index + ', actual: ' + args.length + ')' | ||
} | ||
var err = new TypeError(message); | ||
err.data = data; | ||
return err; | ||
} | ||
/** | ||
* Collection with function references (local shortcuts to functions) | ||
@@ -145,5 +161,16 @@ * @constructor | ||
if (typeof types === 'string') { | ||
this.types = types.split('|').map(function (type) { | ||
return type.trim(); | ||
}); | ||
// parse variable arguments operator (ellipses '...number') | ||
var _types = types.trim(); | ||
var _varArgs = _types.substr(0, 3) === '...'; | ||
if (_varArgs) { | ||
_types = _types.substr(3); | ||
} | ||
if (_types === '') { | ||
this.types = ['any']; | ||
} | ||
else { | ||
this.types = _types.split('|').map(function (type) { | ||
return type.trim(); | ||
}); | ||
} | ||
} | ||
@@ -160,11 +187,9 @@ else if (Array.isArray(types)) { | ||
// parse variable arguments operator (ellipses '...number') | ||
if (this.types[0] !== undefined && this.types[0].substring(0, 3) == '...') { | ||
this.types[0] = this.types[0].substring(3) || 'any'; | ||
this.varArgs = true; | ||
} | ||
else { | ||
this.varArgs = varArgs || false; | ||
} | ||
// can hold a type to which to convert when handling this parameter | ||
this.conversions = []; | ||
// TODO: better implement support for conversions, be able to add conversions via constructor (support a new type Object?) | ||
// variable arguments | ||
this.varArgs = _varArgs || varArgs || false; | ||
// check for any type arguments | ||
@@ -177,16 +202,90 @@ this.anyType = this.types.some(function (type) { | ||
/** | ||
* Order Params | ||
* any type ('any') will be ordered last, and object as second last (as other | ||
* types may be an object as well, like Array). | ||
* | ||
* @param {Param} a | ||
* @param {Param} b | ||
* @returns {number} Returns 1 if a > b, -1 if a < b, and else 0. | ||
*/ | ||
Param.compare = function (a, b) { | ||
if (a.anyType) return 1; | ||
if (a.anyType) return -1; | ||
if (contains(a.types, 'Object')) return 1; | ||
if (contains(b.types, 'Object')) return -1; | ||
if (a.hasConversions()) { | ||
if (b.hasConversions()) { | ||
var ac = a.conversions.filter(function (conversion) { | ||
return conversion !== undefined; | ||
})[0]; | ||
var bc = b.conversions.filter(function (conversion) { | ||
return conversion !== undefined; | ||
})[0]; | ||
return typed.conversions.indexOf(ac) - typed.conversions.indexOf(bc); | ||
} | ||
else { | ||
return 1; | ||
} | ||
} | ||
else { | ||
if (b.hasConversions()) { | ||
return -1; | ||
} | ||
} | ||
return 0; | ||
}; | ||
/** | ||
* Test whether this parameters types overlap an other parameters types. | ||
* @param {Param} other | ||
* @return {boolean} Returns true when there are conflicting types | ||
*/ | ||
Param.prototype.overlapping = function (other) { | ||
return this.types.some(function (type) { | ||
return contains(other.types, type); | ||
}); | ||
}; | ||
/** | ||
* Create a clone of this param | ||
* @returns {Param} A cloned version of this param | ||
* @returns {Param} Returns a cloned version of this param | ||
*/ | ||
Param.prototype.clone = function () { | ||
return new Param(this.types.slice(), this.varArgs); | ||
var param = new Param(this.types.slice(), this.varArgs); | ||
param.conversions = this.conversions.slice(); | ||
return param; | ||
}; | ||
/** | ||
* Test whether this parameter contains conversions | ||
* @returns {boolean} Returns true if the parameter contains one or | ||
* multiple conversions. | ||
*/ | ||
Param.prototype.hasConversions = function () { | ||
return this.conversions.length > 0; | ||
}; | ||
/** | ||
* Return a string representation of this params types, like 'string' or | ||
* 'number | boolean' or '...number' | ||
* @param {boolean} [toConversion] If true, the returned types string | ||
* contains the types where the parameter | ||
* will convert to. If false (default) | ||
* the "from" types are returned | ||
* @returns {string} | ||
*/ | ||
Param.prototype.toString = function () { | ||
return (this.varArgs ? '...' : '') + this.types.join('|'); | ||
Param.prototype.toString = function (toConversion) { | ||
var types = this.types | ||
.map(function (type, i) { | ||
var conversion = this.conversions[i]; | ||
return toConversion && conversion ? conversion.to : type; | ||
}.bind(this)) | ||
.filter(function (type, i, types) { | ||
return types.indexOf(type) === i; // dedupe array | ||
}); | ||
return (this.varArgs ? '...' : '') + types.join('|'); | ||
}; | ||
@@ -196,5 +295,6 @@ | ||
* A function signature | ||
* @param {string | string[]} params Array with the type(s) of each parameter, | ||
* or a comma separated string with types | ||
* @param {function} fn The actual function | ||
* @param {string | string[] | Param[]} params | ||
* Array with the type(s) of each parameter, | ||
* or a comma separated string with types | ||
* @param {function} fn The actual function | ||
* @constructor | ||
@@ -216,3 +316,3 @@ */ | ||
} | ||
// check variable arguments operator '...' | ||
@@ -236,27 +336,57 @@ var withVarArgs = this.params.filter(function (param) { | ||
/** | ||
* Split params with multiple types in separate signatures, | ||
* for example split a Signature "string | number" into two signatures. | ||
* Create a clone of this signature | ||
* @returns {Signature} Returns a cloned version of this signature | ||
*/ | ||
Signature.prototype.clone = function () { | ||
return new Signature(this.params.slice(), this.fn); | ||
}; | ||
/** | ||
* Expand a signature: split params with union types in separate signatures | ||
* For example split a Signature "string | number" into two signatures. | ||
* @return {Signature[]} Returns an array with signatures (at least one) | ||
*/ | ||
Signature.prototype.split = function () { | ||
Signature.prototype.expand = function () { | ||
var signatures = []; | ||
function _iterate(signature, types, index) { | ||
if (index < signature.params.length) { | ||
var param = signature.params[index]; | ||
if (index == signature.params.length - 1 && signature.varArgs) { | ||
// last parameter of a varArgs signature. Do not split the varArgs parameter | ||
_iterate(signature, types.concat(param), index + 1); | ||
function recurse(signature, path) { | ||
if (path.length < signature.params.length) { | ||
var param = signature.params[path.length]; | ||
if (param.varArgs) { | ||
// a variable argument. do not split the types in the parameter | ||
var newParam = param.clone(); | ||
// add conversions to the parameter | ||
// recurse for all conversions | ||
typed.conversions.forEach(function (conversion) { | ||
if (!contains(param.types, conversion.from) && contains(param.types, conversion.to)) { | ||
var i = newParam.types.length; | ||
newParam.types[i] = conversion.from; | ||
newParam.conversions[i] = conversion; | ||
} | ||
}); | ||
recurse(signature, path.concat(newParam)); | ||
} | ||
else { | ||
// split each type in the parameter | ||
param.types.forEach(function (type) { | ||
_iterate(signature, types.concat(new Param(type, param.varArgs)), index + 1); | ||
recurse(signature, path.concat(new Param(type))); | ||
}); | ||
// recurse for all conversions | ||
typed.conversions.forEach(function (conversion) { | ||
if (!contains(param.types, conversion.from) && contains(param.types, conversion.to)) { | ||
var newParam = new Param(conversion.from); | ||
newParam.conversions[0] = conversion; | ||
recurse(signature, path.concat(newParam)); | ||
} | ||
}); | ||
} | ||
} | ||
else { | ||
signatures.push(new Signature(types, signature.fn)); | ||
signatures.push(new Signature(path, signature.fn)); | ||
} | ||
} | ||
_iterate(this, [], 0); | ||
recurse(this, []); | ||
@@ -267,189 +397,175 @@ return signatures; | ||
/** | ||
* A node is used to create a node tree to recursively traverse parameters | ||
* of a function. Nodes have either: | ||
* - Child nodes in a map `types`. | ||
* - No child nodes but a function `fn`, the function to be called for | ||
* This signature. | ||
* @param {Param} type The parameter type of this node | ||
* @param {Node | RootNode} parent | ||
* @constructor | ||
* Compare two signatures. | ||
* | ||
* When two params are equal and contain conversions, they will be sorted | ||
* by lowest index of the first conversions. | ||
* | ||
* @param {Signature} a | ||
* @param {Signature} b | ||
* @returns {number} Returns 1 if a > b, -1 if a < b, and else 0. | ||
*/ | ||
function Node (type, parent) { | ||
this.parent = parent; | ||
this.type = type; | ||
this.fn = null; | ||
this.varArgs = false; // true if variable args '...' | ||
this.childs = {}; | ||
} | ||
Signature.compare = function (a, b) { | ||
if (a.params.length > b.params.length) return 1; | ||
if (a.params.length < b.params.length) return -1; | ||
for (var i = 0; i < a.params.length; i++) { | ||
var cmp = Param.compare(a.params[i], b.params[i]); | ||
if (cmp !== 0) { | ||
return cmp; | ||
} | ||
} | ||
return 0; | ||
}; | ||
/** | ||
* Calculates the maximum depth (level) of nested childs | ||
* @return {number} Returns the maximum depth (zero if no childs, one if | ||
* it has childs without childs, etc) | ||
* Test whether any of the signatures parameters has conversions | ||
* @return {boolean} Returns true when any of the parameters contains | ||
* conversions. | ||
*/ | ||
Node.prototype.depth = function () { | ||
var level = 0; | ||
Object.keys(this.childs).forEach(function (type) { | ||
var childLevel = this.childs[type].depth() + 1; | ||
level = Math.max(level, childLevel); | ||
}.bind(this)); | ||
return level; | ||
Signature.prototype.hasConversions = function () { | ||
return this.params.some(function (param) { | ||
return param.hasConversions(); | ||
}); | ||
}; | ||
/** | ||
* Test recursively whether this Node or any of it's childs need conversions | ||
* Generate the code to invoke this signature | ||
* @param {Refs} refs | ||
* @param {string} prefix | ||
* @returns {string} Returns code | ||
*/ | ||
Node.prototype.hasConversions = function () { | ||
if (this._getConversions().length > 0) { | ||
return true; | ||
} | ||
if (this.type && this.type.varArgs && this._getVarArgConversions().length > 0) { | ||
return true; | ||
} | ||
if (this.childs) { | ||
for (var type in this.childs) { | ||
if (this.childs.hasOwnProperty(type)) { | ||
if (this.childs[type].hasConversions()) { | ||
return true; | ||
} | ||
} | ||
Signature.prototype.toCode = function (refs, prefix) { | ||
var code = []; | ||
var args = this.params.map(function (param, index) { | ||
var conversion = param.conversions[0]; | ||
if (param.varArgs) { | ||
return 'varArgs'; | ||
} | ||
else if (conversion) { | ||
return refs.add(conversion.convert, 'convert') + '(arg' + index + ')'; | ||
} | ||
else { | ||
return 'arg' + index; | ||
} | ||
}).join(', '); | ||
var ref = this.fn ? refs.add(this.fn, 'signature') : undefined; | ||
if (ref) { | ||
return prefix + 'return ' + ref + '(' + args + '); // signature: ' + this.params.join(', '); | ||
} | ||
return false; | ||
return code.join('\n'); | ||
}; | ||
/** | ||
* Returns a string with JavaScript code for this function | ||
* | ||
* @param {{refs: Refs, args: string[], types: Param[], tests: string[], prefix: string, conversions: boolean, exceptions: boolean}} params | ||
* @param {{from: string, to: string, convert: function}} [conversion] | ||
* | ||
* Where: | ||
* {Refs} refs Object to store function references | ||
* {string[]} args Argument names, like ['arg0', 'arg1', ...], | ||
* but can also contain conversions like ['arg0', 'convert1(arg1)'] | ||
* {Param[]} types Array with parameter types parsed so far | ||
* {string[]} tests Type tests, like ['test0', 'test2', ...] | ||
* {string} prefix A number of spaces to prefix for every line of code | ||
* {boolean} conversions A boolean which is true when the generated | ||
* code must do type conversions | ||
* {boolean} exceptions A boolean which is true when the generated code | ||
* must throw exceptions when there is no signature match | ||
* | ||
* @returns {string} code | ||
* | ||
* @protected | ||
* Return a string representation of the signature | ||
* @returns {string} | ||
*/ | ||
Node.prototype._toCode = function (params, conversion) { | ||
var code = []; | ||
Signature.prototype.toString = function () { | ||
return this.params.join(', '); | ||
}; | ||
var prefix = params.prefix; | ||
var ref = this.fn ? params.refs.add(this.fn, 'signature') : undefined; | ||
var arg = this.varArgs ? 'varArgs' : ('arg' + params.args.length); | ||
var type = this.type; | ||
/** | ||
* A group of signatures with the same parameter on given index | ||
* @param {Param[]} path | ||
* @param {Signature} [signature] | ||
* @param {Node[]} childs | ||
* @constructor | ||
*/ | ||
function Node(path, signature, childs) { | ||
this.path = path || []; | ||
this.param = path[path.length - 1] || null; | ||
this.signature = signature || null; | ||
this.childs = childs || []; | ||
} | ||
var test; | ||
var nextArg; | ||
var convert; | ||
if (conversion) { | ||
test = params.refs.add(getTypeTest(conversion.from), 'test'); | ||
convert = params.refs.add(conversion.convert, 'convert'); | ||
nextArg = (type.varArgs === false) ? (convert + '(' + arg + ')') : arg; | ||
} | ||
else { | ||
test = (type.anyType === false) ? params.refs.add(getTypeTest(type.types[0]), 'test') : ''; | ||
convert = null; | ||
nextArg = arg; | ||
} | ||
var comment = '// type: ' + (convert ? (conversion.from + ', convert to ' + type) : type); | ||
/** | ||
* Generate code for this group of signatures | ||
* @param {Refs} refs | ||
* @param {string} prefix | ||
* @param {Node | undefined} [anyType] Sibling of this node with any type parameter | ||
* @returns {string} Returns the code as string | ||
*/ | ||
Node.prototype.toCode = function (refs, prefix, anyType) { | ||
// TODO: split this function in multiple functions, it's too large | ||
var code = []; | ||
var args = params.args.concat(nextArg); | ||
var types = params.types.concat(type); | ||
var tests = params.tests.concat(test); | ||
var nextParams = merge(params, { | ||
args: args, | ||
types: types, | ||
tests: tests | ||
}); | ||
if (this.param) { | ||
var index = this.path.length - 1; | ||
var conversion = this.param.conversions[0]; | ||
var comment = '// type: ' + (conversion ? | ||
(conversion.from + ' (convert to ' + conversion.to + ')') : | ||
this.param); | ||
if (this.varArgs) { | ||
// varArgs cannot have childs, it's the last argument | ||
if (type.anyType) { | ||
if (ref) { | ||
code.push(prefix + 'if (arguments.length >= ' + args.length + ') {'); | ||
// non-root node (path is non-empty) | ||
if (this.param.varArgs) { | ||
if (this.param.anyType) { | ||
// variable arguments with any type | ||
code.push(prefix + 'if (arguments.length > ' + index + ') {'); | ||
code.push(prefix + ' var varArgs = [];'); | ||
code.push(prefix + ' for (var i = ' + (args.length - 1) + '; i < arguments.length; i++) {'); | ||
code.push(prefix + ' for (var i = ' + index + '; i < arguments.length; i++) {'); | ||
code.push(prefix + ' varArgs.push(arguments[i]);'); | ||
code.push(prefix + ' }'); | ||
code.push(prefix + ' return ' + ref + '(' + args.join(', ') + '); // signature: ' + types.join(', ')); | ||
code.push(this.signature.toCode(refs, prefix + ' ')); | ||
code.push(prefix + '}'); | ||
// TODO: throw Exception | ||
} | ||
} | ||
else { | ||
if (ref) { | ||
var varTests = function (tests, arg) { | ||
return tests | ||
else { | ||
// variable arguments with a fixed type | ||
var getTests = function (types, arg) { | ||
return types | ||
.map(function (type) { | ||
var test = params.refs.add(getTypeTest(type), 'test'); | ||
return test + '(' + arg + ')'; | ||
return refs.add(getTypeTest(type), 'test') + '(' + arg + ')'; | ||
}) | ||
.join(' || '); | ||
}; | ||
}.bind(this); | ||
// collect all types (exact types and conversions, excluding types | ||
// handled by node's siblings) | ||
var allTests = this._getVarArgConversions() | ||
.map(function (conversion) { | ||
return conversion.from; | ||
}) | ||
.concat(type.types); | ||
var allTypes = this.param.types; | ||
var exactTypes = allTypes.filter(function (type, i) { | ||
return this.param.conversions[i] === undefined; | ||
}.bind(this)); | ||
code.push(prefix + 'if (' + varTests(allTests, 'arg' + params.args.length) + ') {'); | ||
code.push(prefix + ' var varArgs = [];'); | ||
code.push(prefix + ' for (var i = ' + (args.length - 1) + '; i < arguments.length; i++) {'); | ||
code.push(prefix + ' if (' + varTests(type.types, 'arguments[i]') + ') { // type: ' + type.types.join(' or ')); | ||
code.push(prefix + 'if (' + getTests(allTypes, 'arg' + index) + ') { ' + comment); | ||
code.push(prefix + ' var varArgs = [arg' + index + '];'); | ||
code.push(prefix + ' for (var i = ' + (index + 1) + '; i < arguments.length; i++) {'); | ||
code.push(prefix + ' if (' + getTests(exactTypes, 'arguments[i]') + ') {'); | ||
code.push(prefix + ' varArgs.push(arguments[i]);'); | ||
// iterate over all type conversions (including types handled by this node's siblings) | ||
if (params.conversions) { | ||
this._getVarArgConversions().forEach(function (conversion) { | ||
var test = params.refs.add(getTypeTest(conversion.from), 'test'); | ||
var convert = params.refs.add(conversion.convert, 'convert'); | ||
var comment = '// type: ' + conversion.from + ', convert to ' + conversion.to; | ||
allTypes.forEach(function (type, i) { | ||
var conversion = this.param.conversions[i]; | ||
if (conversion) { | ||
var test = refs.add(getTypeTest(type), 'test'); | ||
var convert = refs.add(conversion.convert, 'convert'); | ||
code.push(prefix + ' }'); | ||
code.push(prefix + ' else if (' + test + '(arguments[i])) { ' + comment); | ||
code.push(prefix + ' else if (' + test + '(arguments[i])) {'); | ||
code.push(prefix + ' varArgs.push(' + convert + '(arguments[i]));'); | ||
}.bind(this)); | ||
} | ||
} | ||
}.bind(this)); | ||
code.push(prefix + ' } else {'); | ||
if (params.exceptions) { | ||
var err = params.refs.add(unexpectedType, 'err'); | ||
code.push(prefix + ' throw ' + err + '(\'' + this.type.types + '\', arguments[i], i); // Unexpected type'); | ||
} | ||
code.push(prefix + ' throw createError(\'\', arguments, i, \'' + allTypes.join(',') + '\');'); | ||
code.push(prefix + ' }'); | ||
code.push(prefix + ' }'); | ||
code.push(prefix + ' return ' + ref + '(' + args.join(', ') + '); // signature: ' + types.join(', ')); | ||
code.push(this.signature.toCode(refs, prefix + ' ')); | ||
code.push(prefix + '}'); | ||
} | ||
} | ||
} | ||
else { | ||
if (type.anyType) { | ||
// any type (ordered last) | ||
code.push(this._innerCode(nextParams)); | ||
} | ||
else { | ||
code.push(prefix + 'if (' + test + '(' + arg + ')) { ' + comment); | ||
code.push(this._innerCode(merge(nextParams, {prefix: prefix + ' '}))); | ||
code.push(prefix + '}'); | ||
if (this.param.anyType) { | ||
// any type | ||
code.push(prefix + '// type: any'); | ||
code.push(this._innerCode(refs, prefix, anyType)); | ||
} | ||
else { | ||
// regular type | ||
var type = this.param.types[0]; | ||
var test = type !== 'any' ? refs.add(getTypeTest(type), 'test') : null; | ||
code.push(prefix + 'if (' + test + '(arg' + index + ')) { ' + comment); | ||
code.push(this._innerCode(refs, prefix + ' ', anyType)); | ||
code.push(prefix + '}'); | ||
} | ||
} | ||
} | ||
else { | ||
// root node (path is empty) | ||
code.push(this._innerCode(refs, prefix, anyType)); | ||
} | ||
@@ -460,48 +576,34 @@ return code.join('\n'); | ||
/** | ||
* Sub function of Node.prototype._toNode | ||
* | ||
* @param {{refs: Refs, args: string[], types: Param[], tests: string[], prefix: string, conversions: boolean, exceptions: boolean}} params | ||
* | ||
* Where: | ||
* {Refs} refs Object to store function references | ||
* {string[]} args Argument names, like ['arg0', 'arg1', ...], | ||
* but can also contain conversions like ['arg0', 'convert1(arg1)'] | ||
* Must include the arg of the current node. | ||
* {Param[]} types Array with parameter types parsed so far | ||
* Must include the type of the current node. | ||
* {string[]} tests Type tests, like ['test0', 'test2', ...] | ||
* Must include the test of the current node. | ||
* {string} prefix A number of spaces to prefix for every line of code | ||
* {boolean} conversions A boolean which is true when the generated | ||
* code must do type conversions | ||
* {boolean} exceptions A boolean which is true when the generated code | ||
* must throw exceptions when there is no signature match | ||
* | ||
* @returns {string} code | ||
* | ||
* @protected | ||
* Generate inner code for this group of signatures. | ||
* This is a helper function of Node.prototype.toCode | ||
* @param {Refs} refs | ||
* @param {string} prefix | ||
* @param {Node | undefined} [anyType] Sibling of this node with any type parameter | ||
* @returns {string} Returns the inner code as string | ||
* @private | ||
*/ | ||
Node.prototype._innerCode = function (params) { | ||
Node.prototype._innerCode = function(refs, prefix, anyType) { | ||
var code = []; | ||
var prefix = params.prefix; | ||
var ref = this.fn ? params.refs.add(this.fn, 'signature') : undefined; | ||
if (ref) { | ||
code.push(prefix + 'if (arguments.length === ' + params.args.length + ') {'); | ||
code.push(prefix + ' return ' + ref + '(' + params.args.join(', ') + '); // signature: ' + params.types.join(', ')); | ||
if (this.signature) { | ||
code.push(prefix + 'if (arguments.length === ' + this.path.length + ') {'); | ||
code.push(this.signature.toCode(refs, prefix + ' ')); | ||
code.push(prefix + '}'); | ||
} | ||
// iterate over the childs | ||
this._getChilds().forEach(function (child) { | ||
code.push(child._toCode(merge(params))); | ||
var nextAnyType = this.childs.filter(function (child) { | ||
return child.param.anyType; | ||
})[0]; | ||
this.childs.forEach(function (child) { | ||
code.push(child.toCode(refs, prefix, nextAnyType)); | ||
}); | ||
// handle conversions | ||
if (params.conversions) { | ||
code = code.concat(this._conversionsToCode(params)); | ||
if (anyType && !this.param.anyType) { | ||
code.push(anyType.toCode(refs, prefix, nextAnyType)); | ||
} | ||
if (params.exceptions) { | ||
code.push(this._exceptions(params)); | ||
var exceptions = this._exceptions(refs, prefix); | ||
if (exceptions) { | ||
code.push(exceptions); | ||
} | ||
@@ -513,323 +615,103 @@ | ||
/** | ||
* Create an unsupported type error | ||
* @param {string} expected String with comma separated types | ||
* @param {*} actual The actual argument | ||
* @param {number} index Index of the argument | ||
* @returns {TypeError} Returns a TypeError | ||
*/ | ||
function unexpectedType (expected, actual, index) { | ||
var arr = expected.split(','); | ||
var message = 'Unexpected type of argument'; | ||
var actualType = getTypeOf(actual); | ||
var err = new TypeError(message + ' (expected: ' + arr.join(' or ') + ', actual: ' + actualType + ', index: ' + index + ')'); | ||
err.data = { | ||
message: message, | ||
expected: arr, | ||
actual: actual, | ||
index: index | ||
}; | ||
return err; | ||
} | ||
/** | ||
* Create a too-few-arguments error | ||
* @param {string} expected String with comma separated types | ||
* @param {number} index index of the argument | ||
* @returns {TypeError} Returns a TypeError | ||
*/ | ||
function tooFewArguments(expected, index) { | ||
var arr = expected.split(','); | ||
var message = 'Too few arguments'; | ||
var err = new TypeError(message + ' (expected: ' + arr.join(' or ') + ', index: ' + index + ')'); | ||
err.data = { | ||
message: message, | ||
expected: arr, | ||
index: index | ||
}; | ||
return err; | ||
} | ||
/** | ||
* Create an too-many-arguments error | ||
* @param {number} expected The expected number of arguments | ||
* @param {number} actual The actual number of arguments | ||
* @returns {TypeError}Returns a TypeError | ||
*/ | ||
function tooManyArguments(expected, actual) { | ||
var message = 'Too many arguments'; | ||
var err = new TypeError(message + ' (expected: ' + expected + ', actual: ' + actual + ')'); | ||
err.data = { | ||
message: message, | ||
expected: expected, | ||
actual: actual | ||
}; | ||
return err; | ||
} | ||
/** | ||
* Create code to throw an error | ||
* | ||
* @param {{refs: Refs, args: string[], types: Param[], tests: string[], prefix: string, conversions: boolean, exceptions: boolean}} params | ||
* | ||
* Where: | ||
* {Refs} refs Object to store function references | ||
* {string[]} args Argument names, like ['arg0', 'arg1', ...], | ||
* but can also contain conversions like ['arg0', 'convert1(arg1)'] | ||
* Must include the arg of the current node. | ||
* {Param[]} types Array with parameter types parsed so far | ||
* Must include the type of the current node. | ||
* {string[]} tests Type tests, like ['test0', 'test2', ...] | ||
* Must include the test of the current node. | ||
* {string} prefix A number of spaces to prefix for every line of code | ||
* {boolean} conversions A boolean which is true when the generated | ||
* code must do type conversions | ||
* {boolean} exceptions A boolean which is true when the generated code | ||
* must throw exceptions when there is no signature match | ||
* | ||
* @returns {string} Code throwing an error | ||
* | ||
* Generate code to throw exceptions | ||
* @param {Refs} refs | ||
* @param {string} prefix | ||
* @returns {string} Returns the inner code as string | ||
* @private | ||
*/ | ||
Node.prototype._exceptions = function (params) { | ||
var code = []; | ||
var prefix = params.prefix; | ||
var argCount = params.args.length; | ||
var arg = 'arg' + params.args.length; | ||
Node.prototype._exceptions = function (refs, prefix) { | ||
var index = this.path.length; | ||
var types = Object.keys(this.childs); | ||
var typeNames = types | ||
.reduce(function (typeNames, type) { | ||
var child = this.childs[type]; | ||
return typeNames.concat(child.type.types); | ||
}.bind(this), []); | ||
var firstChild = types.length > 0 ? this.childs[types[0]] : undefined; | ||
if (firstChild && firstChild.varArgs) { | ||
// variable arguments | ||
code.push(prefix + 'if (arguments.length === ' + argCount + ') {'); | ||
code.push(prefix + ' throw ' + params.refs.add(tooFewArguments, 'err') + | ||
'(\'' + typeNames.join(',') + '\', arguments.length); // Too few arguments'); | ||
code.push(prefix + '} else {'); | ||
code.push(prefix + ' throw ' + params.refs.add(unexpectedType, 'err') + | ||
'(\'' + typeNames.join(',') + '\', ' + arg + ', ' + argCount + '); // Unexpected type'); | ||
code.push(prefix + '}'); | ||
// TODO: add function name to exceptions | ||
if (this.childs.length === 0) { | ||
// TODO: can this condition be simplified? (we have a fall-through here) | ||
return [ | ||
prefix + 'if (arguments.length > ' + index + ') {', | ||
prefix + ' throw createError(\'\', arguments, ' + index + ')', | ||
prefix + '}' | ||
].join('\n'); | ||
} | ||
else if (types.length === 0) { | ||
// no childs | ||
code.push(prefix + 'if (arguments.length > ' + argCount + ') {'); | ||
code.push(prefix + ' throw ' + params.refs.add(tooManyArguments, 'err') + | ||
'(' + argCount + ', arguments.length); // Too many arguments'); | ||
code.push(prefix + '}'); | ||
} | ||
else if (types.indexOf('any') !== -1) { | ||
// any type | ||
code.push(prefix + 'throw ' + params.refs.add(tooFewArguments, 'err') + | ||
'(\'any\', arguments.length); // Too few arguments'); | ||
} | ||
else { | ||
// regular type | ||
// TODO: add "Actual: ..." to the error message | ||
code.push(prefix + 'if (arguments.length === ' + argCount + ') {'); | ||
code.push(prefix + ' throw ' + params.refs.add(tooFewArguments, 'err') + | ||
'(\'' + typeNames.join(',') + '\', arguments.length); // Too few arguments'); | ||
code.push(prefix + '}'); | ||
code.push(prefix + 'else {'); | ||
code.push(prefix + ' throw ' + params.refs.add(unexpectedType, 'err') + | ||
'(\'' + typeNames.join(',') + '\', ' + arg + ', ' + argCount + '); // Unexpected type'); | ||
code.push(prefix + '}'); | ||
} | ||
var types = this.childs.reduce(function (types, node) { | ||
node.param && node.param.types.forEach(function (type) { | ||
if (types.indexOf(type) === -1) { | ||
types.push(type); | ||
} | ||
}); | ||
return code.join('\n'); | ||
}; | ||
return types; | ||
}, []); | ||
/** | ||
* Create a code representation for iterating over conversions | ||
* | ||
* @param {{refs: Refs, args: string[], types: Param[], tests: string[], prefix: string, conversions: boolean, exceptions: boolean}} params | ||
* | ||
* Where: | ||
* {Refs} refs Object to store function references | ||
* {string[]} args Argument names, like ['arg0', 'arg1', ...], | ||
* but can also contain conversions like ['arg0', 'convert1(arg1)'] | ||
* {Param[]} types Array with parameter types parsed so far | ||
* {string[]} tests Type tests, like ['test0', 'test2', ...] | ||
* {string} prefix A number of spaces to prefix for every line of code | ||
* {boolean} conversions A boolean which is true when the generated | ||
* code must do type conversions | ||
* {boolean} exceptions A boolean which is true when the generated code | ||
* must throw exceptions when there is no signature match | ||
* | ||
* @returns {string[]} Returns an array with code lines | ||
* | ||
* @protected | ||
*/ | ||
Node.prototype._conversionsToCode = function (params) { | ||
var code = []; | ||
// iterate over the type conversions | ||
this._getConversions().forEach(function (conversion) { | ||
var type = conversion.to; | ||
var child = this.childs[type]; | ||
code.push(child._toCode(params, conversion)); | ||
}.bind(this)); | ||
return code; | ||
}; | ||
/** | ||
* Get the childs of this node ordered by type | ||
* @return {Node[]} Returns an array with Nodes | ||
* @protected | ||
*/ | ||
Node.prototype._getChilds = function () { | ||
return Object.keys(this.childs) | ||
.map(function (type) { | ||
return this.childs[type]; | ||
}.bind(this)) | ||
.sort(function (a, b) { | ||
return compareParams(a.type, b.type); | ||
}) | ||
}; | ||
/** | ||
* Get the conversions relevant for this Node | ||
* @returns {Array} Array with conversion objects | ||
* @protected | ||
*/ | ||
Node.prototype._getConversions = function () { | ||
// if there is a varArgs child, there is no need to do conversions separately, | ||
// that is handled by the varArg loop | ||
var hasVarArgs = this._getChilds().some(function (child) { | ||
return child.type.varArgs; | ||
}); | ||
if (hasVarArgs) { | ||
return []; | ||
return prefix + 'throw createError(\'\', arguments, ' + index + ', \'' + types.join(',') + '\');'; | ||
} | ||
// filter the relevant type conversions | ||
var handled = {}; | ||
return typed.conversions | ||
.filter(function (conversion) { | ||
if (this.childs[conversion.from] === undefined && | ||
this.childs[conversion.to] !== undefined && | ||
!handled[conversion.from]) { | ||
handled[conversion.from] = true; | ||
return true; | ||
} | ||
else { | ||
return false; | ||
} | ||
}.bind(this)); | ||
}; | ||
/** | ||
* Get the conversions relevant for a Node with variable arguments | ||
* @returns {Array} Array with conversion objects | ||
* @protected | ||
* Split all raw signatures into an array with expanded Signatures | ||
* @param {Object.<string, function>} rawSignatures | ||
* @return {Signature[]} Returns an array with expanded signatures | ||
*/ | ||
Node.prototype._getVarArgConversions = function () { | ||
// filter the relevant type conversions | ||
var handled = {}; | ||
return typed.conversions | ||
.filter(function (conversion) { | ||
if (this.type.types.indexOf(conversion.from) === -1 && | ||
this.type.types.indexOf(conversion.to) !== -1 && | ||
!this.parent.childs[conversion.from] && | ||
!handled[conversion.from]) { | ||
handled[conversion.from] = true; | ||
return true; | ||
} | ||
else { | ||
return false; | ||
} | ||
}.bind(this)) | ||
}; | ||
function parseSignatures(rawSignatures) { | ||
var map = Object.keys(rawSignatures) | ||
.reduce(function (signatures, types) { | ||
var fn = rawSignatures[types]; | ||
var signature = new Signature(types, fn); | ||
/** | ||
* The root node of a node tree | ||
* @param {string} [name] Optional function name | ||
* @constructor | ||
*/ | ||
function RootNode(name) { | ||
this.name = name || ''; | ||
this.fn = null; | ||
this.childs = {}; | ||
} | ||
signature.expand().forEach(function (signature) { | ||
var key = signature.toString(); | ||
if (signatures[key] === undefined) { | ||
signatures[key] = signature; | ||
} | ||
else { | ||
var cmp = Signature.compare(signature, signatures[key]); | ||
if (cmp < 0) { | ||
// override if sorted first | ||
signatures[key] = signature; | ||
} | ||
else if (cmp === 0) { | ||
throw new Error('Signature "' + key + '" is defined twice'); | ||
} | ||
// else: just ignore | ||
} | ||
}); | ||
RootNode.prototype = Object.create(Node.prototype); | ||
return signatures; | ||
}, {}); | ||
/** | ||
* Returns a string with JavaScript code for this function | ||
* @param {Refs} refs Object to store function references | ||
* @return {string} code | ||
*/ | ||
RootNode.prototype.toCode = function (refs) { | ||
var code = []; | ||
// convert from map to array | ||
var arr = Object.keys(map).map(function (types) { | ||
return map[types]; | ||
}); | ||
// create an array with all argument names | ||
var argCount = this.depth(); | ||
var args = []; | ||
for (var i = 0; i < argCount; i++) { | ||
args[i] = 'arg' + i; | ||
} | ||
// filter redundant conversions from signatures with varArgs | ||
// TODO: simplify this loop or move it to a separate function | ||
arr.forEach(function (signature) { | ||
if (signature.varArgs) { | ||
var index = signature.params.length - 1; | ||
var param = signature.params[index]; | ||
// check whether the function at hand needs conversions | ||
var hasConversions = this.hasConversions(); | ||
var t = 0; | ||
while (t < param.types.length) { | ||
if (param.conversions[t]) { | ||
var type = param.types[t]; | ||
var exists = arr.some(function (other) { | ||
var p = other.params[index]; | ||
// function begin | ||
code.push('return function ' + this.name + '(' + args.join(', ') + ') {'); | ||
return other !== signature && p && | ||
contains(p.types, type) && // FIXME: mutable variable warning | ||
!p.conversions[index]; | ||
}); | ||
// function signature with zero arguments | ||
var ref = this.fn ? refs.add(this.fn, 'signature') : undefined; | ||
if (ref) { | ||
code.push(' if (arguments.length === 0) {'); | ||
code.push(' return ' + ref + '(); // signature: (empty)'); | ||
code.push(' }'); | ||
} | ||
if (exists) { | ||
param.types.splice(t, 1); | ||
param.conversions.splice(t, 1); | ||
t--; | ||
} | ||
} | ||
t++; | ||
} | ||
} | ||
}); | ||
var params = { | ||
refs: refs, | ||
args: [], | ||
types: [], | ||
tests: [], | ||
prefix: ' ' | ||
}; | ||
// matches | ||
code.push(this._getChilds().map(function (child) { | ||
return child._toCode(merge(params, { | ||
conversions: true, | ||
exceptions: true | ||
})); | ||
}).join('\n')); | ||
// FIXME: variable any type argument matching all argument types instead of the left over types not defined by other signatures. | ||
// conversions | ||
if (hasConversions) { | ||
code = code.concat(this._conversionsToCode(merge(params, { | ||
conversions: true, | ||
exceptions: true | ||
}))); | ||
} | ||
// function end | ||
code.push(this._exceptions(params)); | ||
code.push('}'); | ||
return code.join('\n'); | ||
}; | ||
/** | ||
* Split all raw signatures into an array with (split) Signatures | ||
* @param {Object.<string, function>} rawSignatures | ||
* @return {Signature[]} Returns an array with split signatures | ||
*/ | ||
function parseSignatures(rawSignatures) { | ||
return Object.keys(rawSignatures).reduce(function (signatures, params) { | ||
var fn = rawSignatures[params]; | ||
var signature = new Signature(params, fn); | ||
return signatures.concat(signature.split()); | ||
}, []); | ||
return arr; | ||
} | ||
@@ -840,53 +722,83 @@ | ||
* @param {Signature[]} signatures An array with split signatures | ||
* @return {Object} Returns a map with normalized signatures | ||
* @return {Object.<string, function>} Returns a map with normalized | ||
* signatures as key, and the function | ||
* as value. | ||
*/ | ||
function normalizeSignatures(signatures) { | ||
var normalized = {}; | ||
signatures.map(function (entry) { | ||
var signature = entry.params.join(','); | ||
if (signature in normalized) { | ||
throw new Error('Signature "' + signature + '" defined twice'); | ||
function mapSignatures(signatures) { | ||
return signatures.reduce(function (normalized, signature) { | ||
var params = signature.params.join(','); | ||
if (signature.fn) { | ||
normalized[params] = signature.fn; | ||
} | ||
normalized[signature] = entry.fn; | ||
}); | ||
return normalized; | ||
return normalized; | ||
}, {}); | ||
} | ||
/** | ||
* Parse an object with signatures. Creates a recursive node tree for | ||
* traversing the number and type of parameters. | ||
* @param {string} [name] Function name. Optional | ||
* @param {Signature[]} signatures An array with split signatures | ||
* @return {RootNode} Returns a node tree | ||
* Parse signatures recursively in a node tree. | ||
* @param {Signature[]} signatures Array with expanded signatures | ||
* @param {Param[]} path Traversed path of parameter types | ||
* @return {Node} Returns a node tree | ||
*/ | ||
function parseTree(name, signatures) { | ||
var root = new RootNode(name); | ||
function parseTree(signatures, path) { | ||
var index = path.length; | ||
signatures.forEach(function (signature) { | ||
var params = signature.params.concat([]); | ||
// filter the signatures with the correct number of params | ||
var signature = signatures.filter(function (signature) { | ||
return signature.params.length === index; | ||
})[0]; // there can be only one signature | ||
// loop over all parameters, create a nested structure | ||
var node = root; | ||
while(params.length > 0) { | ||
var param = params.shift(); | ||
var type = param.toString(); | ||
// recurse over the signatures | ||
var childs = signatures | ||
.filter(function (signature) { | ||
return signature.params[index] != undefined; | ||
}) | ||
.sort(function (a, b) { | ||
return Param.compare(a.params[index], b.params[index]); | ||
}) | ||
.reduce(function (entries, signature) { | ||
// group signatures with the same param at current index | ||
var param = signature.params[index]; | ||
var existing = entries.filter(function (entry) { | ||
return entry.param.overlapping(param); | ||
})[0]; | ||
var child = node.childs[type]; | ||
if (child === undefined) { | ||
child = node.childs[type] = new Node(param, node); | ||
} | ||
node = child; | ||
} | ||
if (existing) { | ||
if (existing.param.varArgs) { | ||
throw new Error('Conflicting types "' + existing.param + '" and "' + param + '"'); | ||
} | ||
existing.signatures.push(signature); | ||
} | ||
else { | ||
entries.push({ | ||
param: param, | ||
signatures: [signature] | ||
}); | ||
} | ||
// add the function as leaf of the innermost node | ||
node.fn = signature.fn; | ||
node.varArgs = signature.varArgs; | ||
}); | ||
return entries; | ||
}, []) | ||
.map(function (entry) { | ||
return parseTree(entry.signatures, path.concat(entry.param)) | ||
}); | ||
return root; | ||
return new Node(path, signature, childs); | ||
} | ||
/** | ||
* Generate an array like ['arg0', 'arg1', 'arg2'] | ||
* @param {number} count Number of arguments to generate | ||
* @returns {Array} Returns an array with argument names | ||
*/ | ||
function getArgs(count) { | ||
// create an array with all argument names | ||
var args = []; | ||
for (var i = 0; i < count; i++) { | ||
args[i] = 'arg' + i; | ||
} | ||
return args; | ||
} | ||
/** | ||
* Compose a function from sub-functions each handling a single type signature. | ||
@@ -907,6 +819,4 @@ * Signatures: | ||
// parse signatures, create a node tree | ||
// parse signatures, expand them | ||
var _signatures = parseSignatures(signatures); | ||
var tree = parseTree(name, _signatures); | ||
if (_signatures.length == 0) { | ||
@@ -916,13 +826,22 @@ throw new Error('No signatures provided'); | ||
//console.log('TREE\n' + JSON.stringify(tree, null, 2)); // TODO: cleanup | ||
//console.log('TREE', tree); // TODO: cleanup | ||
// parse signatures into a node tree | ||
var node = parseTree(_signatures, []); | ||
var treeCode = tree.toCode(refs); | ||
var refsCode = refs.toCode(); | ||
//var util = require('util'); | ||
//console.log('ROOT'); | ||
//console.log(util.inspect(node, { depth: null })); | ||
// generate JavaScript code | ||
// generate code for the typed function | ||
var code = []; | ||
var _name = name || ''; | ||
var _args = getArgs(maxParams(_signatures)); | ||
code.push('function ' + _name + '(' + _args.join(', ') + ') {'); | ||
code.push(node.toCode(refs, ' ')); | ||
code.push('}'); | ||
// generate code for the factory function | ||
var factory = [ | ||
'(function (' + refs.name + ') {', | ||
refsCode, | ||
treeCode, | ||
refs.toCode(), | ||
'return ' + code.join('\n'), | ||
'})' | ||
@@ -937,3 +856,3 @@ ].join('\n'); | ||
// attach the signatures with sub-functions to the constructed function | ||
fn.signatures = normalizeSignatures(_signatures); // normalized signatures | ||
fn.signatures = mapSignatures(_signatures); | ||
@@ -958,2 +877,13 @@ return fn; | ||
/** | ||
* Calculate the maximum number of parameters in givens signatures | ||
* @param {Signature[]} signatures | ||
* @returns {number} The maximum number of parameters | ||
*/ | ||
function maxParams(signatures) { | ||
return signatures.reduce(function (max, signature) { | ||
return Math.max(max, signature.params.length); | ||
}, 0); | ||
} | ||
/** | ||
* Get the type of a value | ||
@@ -965,9 +895,21 @@ * @param {*} x | ||
for (var type in types) { | ||
if (types.hasOwnProperty(type)) { | ||
if (types.hasOwnProperty(type) && type !== 'Object') { | ||
// Array and Date are also Object, so test for Object afterwards | ||
if (types[type](x)) return type; | ||
} | ||
} | ||
if (types['Object'](x)) return type; | ||
return 'unknown'; | ||
} | ||
/** | ||
* Test whether an array contains some entry | ||
* @param {Array} array | ||
* @param {*} entry | ||
* @return {boolean} Returns true if array contains entry, false if not. | ||
*/ | ||
function contains(array, entry) { | ||
return array.indexOf(entry) !== -1; | ||
} | ||
// configuration | ||
@@ -1030,3 +972,3 @@ var config = {}; | ||
if (signatures.hasOwnProperty(signature)) { | ||
err = new Error('Conflicting signatures: "' + signature + '" is defined twice.'); | ||
err = new Error('Signature "' + signature + '" is defined twice'); | ||
err.data = {signature: signature}; | ||
@@ -1068,2 +1010,1 @@ throw err; | ||
})); | ||
@@ -1,1 +0,1 @@ | ||
!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():e.typed=t()}(this,function(){"use strict";function compareParams(e,t){return e.anyType?1:e.anyType?-1:-1!==e.types.indexOf("Object")?1:-1!==t.types.indexOf("Object")?-1:0}function merge(){for(var e={},t=0;t<arguments.length;t++){var r=arguments[t];for(var n in r)r.hasOwnProperty(n)&&(e[n]=r[n])}return e}function getTypeTest(e){var t=typed.types[e];if(!t){var r=Object.keys(typed.types).filter(function(t){return t.toLowerCase()==e.toLowerCase()}).map(function(e){return'"'+e+'"'});throw new Error('Unknown type "'+e+'"'+(r.length?". Did you mean "+r.join(", or ")+"?":""))}return t}function Refs(e){this.name=e||"refs",this.categories={}}function Param(e,t){if("string"==typeof e)this.types=e.split("|").map(function(e){return e.trim()});else{if(!Array.isArray(e)){if(e instanceof Param)return e.clone();throw new Error("String or Array expected")}this.types=e}void 0!==this.types[0]&&"..."==this.types[0].substring(0,3)?(this.types[0]=this.types[0].substring(3)||"any",this.varArgs=!0):this.varArgs=t||!1,this.anyType=this.types.some(function(e){return"any"==e})}function Signature(e,t){if("string"==typeof e)this.params=""!==e?e.split(",").map(function(e){return new Param(e)}):[];else{if(!Array.isArray(e))throw new Error("string or Array expected");this.params=e.map(function(e){return new Param(e)})}var r=this.params.filter(function(e){return e.varArgs});if(0===r.length)this.varArgs=!1;else{if(r[0]!==this.params[this.params.length-1])throw new SyntaxError('Unexpected variable arguments operator "..."');this.varArgs=!0}this.fn=t}function Node(e,t){this.parent=t,this.type=e,this.fn=null,this.varArgs=!1,this.childs={}}function unexpectedType(e,t,r){var n=e.split(","),s="Unexpected type of argument",i=getTypeOf(t),o=new TypeError(s+" (expected: "+n.join(" or ")+", actual: "+i+", index: "+r+")");return o.data={message:s,expected:n,actual:t,index:r},o}function tooFewArguments(e,t){var r=e.split(","),n="Too few arguments",s=new TypeError(n+" (expected: "+r.join(" or ")+", index: "+t+")");return s.data={message:n,expected:r,index:t},s}function tooManyArguments(e,t){var r="Too many arguments",n=new TypeError(r+" (expected: "+e+", actual: "+t+")");return n.data={message:r,expected:e,actual:t},n}function RootNode(e){this.name=e||"",this.fn=null,this.childs={}}function parseSignatures(e){return Object.keys(e).reduce(function(t,r){var n=e[r],s=new Signature(r,n);return t.concat(s.split())},[])}function normalizeSignatures(e){var t={};return e.map(function(e){var r=e.params.join(",");if(r in t)throw new Error('Signature "'+r+'" defined twice');t[r]=e.fn}),t}function parseTree(e,t){var r=new RootNode(e);return t.forEach(function(e){for(var t=e.params.concat([]),n=r;t.length>0;){var s=t.shift(),i=s.toString(),o=n.childs[i];void 0===o&&(o=n.childs[i]=new Node(s,n)),n=o}n.fn=e.fn,n.varArgs=e.varArgs}),r}function _typed(name,signatures){var refs=new Refs,_signatures=parseSignatures(signatures),tree=parseTree(name,_signatures);if(0==_signatures.length)throw new Error("No signatures provided");var treeCode=tree.toCode(refs),refsCode=refs.toCode(),factory=["(function ("+refs.name+") {",refsCode,treeCode,"})"].join("\n"),fn=eval(factory)(refs);return fn.signatures=normalizeSignatures(_signatures),fn}function getTypeOf(e){for(var t in types)if(types.hasOwnProperty(t)&&types[t](e))return t;return"unknown"}Refs.prototype.add=function(e,t){var r=t||"fn";this.categories[r]||(this.categories[r]=[]);var n=this.categories[r].indexOf(e);return-1==n&&(n=this.categories[r].length,this.categories[r].push(e)),r+n},Refs.prototype.toCode=function(){var e=[],t=this.name+".categories",r=this.categories;return Object.keys(r).forEach(function(n){r[n].forEach(function(r,s){e.push("var "+n+s+" = "+t+"['"+n+"']["+s+"];")})}),e.join("\n")},Param.prototype.clone=function(){return new Param(this.types.slice(),this.varArgs)},Param.prototype.toString=function(){return(this.varArgs?"...":"")+this.types.join("|")},Signature.prototype.split=function(){function e(r,n,s){if(s<r.params.length){var i=r.params[s];s==r.params.length-1&&r.varArgs?e(r,n.concat(i),s+1):i.types.forEach(function(t){e(r,n.concat(new Param(t,i.varArgs)),s+1)})}else t.push(new Signature(n,r.fn))}var t=[];return e(this,[],0),t},Node.prototype.depth=function(){var e=0;return Object.keys(this.childs).forEach(function(t){var r=this.childs[t].depth()+1;e=Math.max(e,r)}.bind(this)),e},Node.prototype.hasConversions=function(){if(this._getConversions().length>0)return!0;if(this.type&&this.type.varArgs&&this._getVarArgConversions().length>0)return!0;if(this.childs)for(var e in this.childs)if(this.childs.hasOwnProperty(e)&&this.childs[e].hasConversions())return!0;return!1},Node.prototype._toCode=function(e,t){var r,n,s,i=[],o=e.prefix,a=this.fn?e.refs.add(this.fn,"signature"):void 0,u=this.varArgs?"varArgs":"arg"+e.args.length,p=this.type;t?(r=e.refs.add(getTypeTest(t.from),"test"),s=e.refs.add(t.convert,"convert"),n=p.varArgs===!1?s+"("+u+")":u):(r=p.anyType===!1?e.refs.add(getTypeTest(p.types[0]),"test"):"",s=null,n=u);var h="// type: "+(s?t.from+", convert to "+p:p),f=e.args.concat(n),c=e.types.concat(p),g=e.tests.concat(r),d=merge(e,{args:f,types:c,tests:g});if(this.varArgs){if(p.anyType)a&&(i.push(o+"if (arguments.length >= "+f.length+") {"),i.push(o+" var varArgs = [];"),i.push(o+" for (var i = "+(f.length-1)+"; i < arguments.length; i++) {"),i.push(o+" varArgs.push(arguments[i]);"),i.push(o+" }"),i.push(o+" return "+a+"("+f.join(", ")+"); // signature: "+c.join(", ")),i.push(o+"}"));else if(a){var y=function(t,r){return t.map(function(t){var n=e.refs.add(getTypeTest(t),"test");return n+"("+r+")"}).join(" || ")},v=this._getVarArgConversions().map(function(e){return e.from}).concat(p.types);if(i.push(o+"if ("+y(v,"arg"+e.args.length)+") {"),i.push(o+" var varArgs = [];"),i.push(o+" for (var i = "+(f.length-1)+"; i < arguments.length; i++) {"),i.push(o+" if ("+y(p.types,"arguments[i]")+") { // type: "+p.types.join(" or ")),i.push(o+" varArgs.push(arguments[i]);"),e.conversions&&this._getVarArgConversions().forEach(function(t){var r=e.refs.add(getTypeTest(t.from),"test"),n=e.refs.add(t.convert,"convert"),s="// type: "+t.from+", convert to "+t.to;i.push(o+" }"),i.push(o+" else if ("+r+"(arguments[i])) { "+s),i.push(o+" varArgs.push("+n+"(arguments[i]));")}.bind(this)),i.push(o+" } else {"),e.exceptions){var m=e.refs.add(unexpectedType,"err");i.push(o+" throw "+m+"('"+this.type.types+"', arguments[i], i); // Unexpected type")}i.push(o+" }"),i.push(o+" }"),i.push(o+" return "+a+"("+f.join(", ")+"); // signature: "+c.join(", ")),i.push(o+"}")}}else p.anyType?i.push(this._innerCode(d)):(i.push(o+"if ("+r+"("+u+")) { "+h),i.push(this._innerCode(merge(d,{prefix:o+" "}))),i.push(o+"}"));return i.join("\n")},Node.prototype._innerCode=function(e){var t=[],r=e.prefix,n=this.fn?e.refs.add(this.fn,"signature"):void 0;return n&&(t.push(r+"if (arguments.length === "+e.args.length+") {"),t.push(r+" return "+n+"("+e.args.join(", ")+"); // signature: "+e.types.join(", ")),t.push(r+"}")),this._getChilds().forEach(function(r){t.push(r._toCode(merge(e)))}),e.conversions&&(t=t.concat(this._conversionsToCode(e))),e.exceptions&&t.push(this._exceptions(e)),t.join("\n")},Node.prototype._exceptions=function(e){var t=[],r=e.prefix,n=e.args.length,s="arg"+e.args.length,i=Object.keys(this.childs),o=i.reduce(function(e,t){var r=this.childs[t];return e.concat(r.type.types)}.bind(this),[]),a=i.length>0?this.childs[i[0]]:void 0;return a&&a.varArgs?(t.push(r+"if (arguments.length === "+n+") {"),t.push(r+" throw "+e.refs.add(tooFewArguments,"err")+"('"+o.join(",")+"', arguments.length); // Too few arguments"),t.push(r+"} else {"),t.push(r+" throw "+e.refs.add(unexpectedType,"err")+"('"+o.join(",")+"', "+s+", "+n+"); // Unexpected type"),t.push(r+"}")):0===i.length?(t.push(r+"if (arguments.length > "+n+") {"),t.push(r+" throw "+e.refs.add(tooManyArguments,"err")+"("+n+", arguments.length); // Too many arguments"),t.push(r+"}")):-1!==i.indexOf("any")?t.push(r+"throw "+e.refs.add(tooFewArguments,"err")+"('any', arguments.length); // Too few arguments"):(t.push(r+"if (arguments.length === "+n+") {"),t.push(r+" throw "+e.refs.add(tooFewArguments,"err")+"('"+o.join(",")+"', arguments.length); // Too few arguments"),t.push(r+"}"),t.push(r+"else {"),t.push(r+" throw "+e.refs.add(unexpectedType,"err")+"('"+o.join(",")+"', "+s+", "+n+"); // Unexpected type"),t.push(r+"}")),t.join("\n")},Node.prototype._conversionsToCode=function(e){var t=[];return this._getConversions().forEach(function(r){var n=r.to,s=this.childs[n];t.push(s._toCode(e,r))}.bind(this)),t},Node.prototype._getChilds=function(){return Object.keys(this.childs).map(function(e){return this.childs[e]}.bind(this)).sort(function(e,t){return compareParams(e.type,t.type)})},Node.prototype._getConversions=function(){var e=this._getChilds().some(function(e){return e.type.varArgs});if(e)return[];var t={};return typed.conversions.filter(function(e){return void 0!==this.childs[e.from]||void 0===this.childs[e.to]||t[e.from]?!1:(t[e.from]=!0,!0)}.bind(this))},Node.prototype._getVarArgConversions=function(){var e={};return typed.conversions.filter(function(t){return-1!==this.type.types.indexOf(t.from)||-1===this.type.types.indexOf(t.to)||this.parent.childs[t.from]||e[t.from]?!1:(e[t.from]=!0,!0)}.bind(this))},RootNode.prototype=Object.create(Node.prototype),RootNode.prototype.toCode=function(e){for(var t=[],r=this.depth(),n=[],s=0;r>s;s++)n[s]="arg"+s;var i=this.hasConversions();t.push("return function "+this.name+"("+n.join(", ")+") {");var o=this.fn?e.add(this.fn,"signature"):void 0;o&&(t.push(" if (arguments.length === 0) {"),t.push(" return "+o+"(); // signature: (empty)"),t.push(" }"));var a={refs:e,args:[],types:[],tests:[],prefix:" "};return t.push(this._getChilds().map(function(e){return e._toCode(merge(a,{conversions:!0,exceptions:!0}))}).join("\n")),i&&(t=t.concat(this._conversionsToCode(merge(a,{conversions:!0,exceptions:!0})))),t.push(this._exceptions(a)),t.push("}"),t.join("\n")};var types={"null":function(e){return null===e},undefined:function(e){return void 0===e},"boolean":function(e){return"boolean"==typeof e},number:function(e){return"number"==typeof e},string:function(e){return"string"==typeof e},"function":function(e){return"function"==typeof e},Array:function(e){return Array.isArray(e)},Date:function(e){return e instanceof Date},RegExp:function(e){return e instanceof RegExp},Object:function(e){return"object"==typeof e}},config={},conversions=[],typed={config:config,types:types,conversions:conversions};return typed=_typed("typed",{Object:function(e){return _typed(null,e)},"string, Object":_typed,"string, function":function(e,t){var r={};return r[e]=t,_typed(t.name||null,r)},"string, string, function":function(e,t,r){var n={};return n[t]=r,_typed(e,n)},"...function":function(e){var t="",r={};return e.forEach(function(e,n){var s;if("object"!=typeof e.signatures)throw s=new TypeError("Function is no typed-function (index: "+n+")"),s.data={index:n},s;for(var i in e.signatures)if(e.signatures.hasOwnProperty(i)){if(r.hasOwnProperty(i))throw s=new Error('Conflicting signatures: "'+i+'" is defined twice.'),s.data={signature:i},s;r[i]=e.signatures[i]}if(""!=e.name)if(""==t)t=e.name;else if(t!=e.name)throw s=new Error("Function names do not match (expected: "+t+", actual: "+e.name+")"),s.data={actual:e.name,expected:t},s}),_typed(t,r)}}),typed.config=config,typed.types=types,typed.conversions=conversions,typed}); | ||
!function(n){"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():window.typed=n()}(function(){"use strict";function getTypeTest(n){var r=typed.types[n];if(!r){var t=Object.keys(typed.types).filter(function(r){return r.toLowerCase()==n.toLowerCase()}).map(function(n){return'"'+n+'"'});throw new Error('Unknown type "'+n+'"'+(t.length?". Did you mean "+t.join(", or ")+"?":""))}return r}function createError(n,r,t,e){var s,i=r[t],a=getTypeOf(i),o=e?e.split(","):null,u=o&&contains(o,"any"),p={fn:n,index:t,actual:i,expected:o};s=o?r.length>t&&!u?"Unexpected type of argument (expected: "+o.join(" or ")+", actual: "+a+", index: "+t+")":"Too few arguments (expected: "+o.join(" or ")+", index: "+t+")":"Too many arguments (expected: "+t+", actual: "+r.length+")";var c=new TypeError(s);return c.data=p,c}function Refs(n){this.name=n||"refs",this.categories={}}function Param(n,r){if("string"==typeof n){var t=n.trim(),e="..."===t.substr(0,3);e&&(t=t.substr(3)),this.types=""===t?["any"]:t.split("|").map(function(n){return n.trim()})}else{if(!Array.isArray(n)){if(n instanceof Param)return n.clone();throw new Error("String or Array expected")}this.types=n}this.conversions=[],this.varArgs=e||r||!1,this.anyType=this.types.some(function(n){return"any"==n})}function Signature(n,r){if("string"==typeof n)this.params=""!==n?n.split(",").map(function(n){return new Param(n)}):[];else{if(!Array.isArray(n))throw new Error("string or Array expected");this.params=n.map(function(n){return new Param(n)})}var t=this.params.filter(function(n){return n.varArgs});if(0===t.length)this.varArgs=!1;else{if(t[0]!==this.params[this.params.length-1])throw new SyntaxError('Unexpected variable arguments operator "..."');this.varArgs=!0}this.fn=r}function Node(n,r,t){this.path=n||[],this.param=n[n.length-1]||null,this.signature=r||null,this.childs=t||[]}function parseSignatures(n){var r=Object.keys(n).reduce(function(r,t){var e=n[t],s=new Signature(t,e);return s.expand().forEach(function(n){var t=n.toString();if(void 0===r[t])r[t]=n;else{var e=Signature.compare(n,r[t]);if(0>e)r[t]=n;else if(0===e)throw new Error('Signature "'+t+'" is defined twice')}}),r},{}),t=Object.keys(r).map(function(n){return r[n]});return t.forEach(function(n){if(n.varArgs)for(var r=n.params.length-1,e=n.params[r],s=0;s<e.types.length;){if(e.conversions[s]){var i=e.types[s],a=t.some(function(t){var e=t.params[r];return t!==n&&e&&contains(e.types,i)&&!e.conversions[r]});a&&(e.types.splice(s,1),e.conversions.splice(s,1),s--)}s++}}),t}function mapSignatures(n){return n.reduce(function(n,r){var t=r.params.join(",");return r.fn&&(n[t]=r.fn),n},{})}function parseTree(n,r){var t=r.length,e=n.filter(function(n){return n.params.length===t})[0],s=n.filter(function(n){return void 0!=n.params[t]}).sort(function(n,r){return Param.compare(n.params[t],r.params[t])}).reduce(function(n,r){var e=r.params[t],s=n.filter(function(n){return n.param.overlapping(e)})[0];if(s){if(s.param.varArgs)throw new Error('Conflicting types "'+s.param+'" and "'+e+'"');s.signatures.push(r)}else n.push({param:e,signatures:[r]});return n},[]).map(function(n){return parseTree(n.signatures,r.concat(n.param))});return new Node(r,e,s)}function getArgs(n){for(var r=[],t=0;n>t;t++)r[t]="arg"+t;return r}function _typed(name,signatures){var refs=new Refs,_signatures=parseSignatures(signatures);if(0==_signatures.length)throw new Error("No signatures provided");var node=parseTree(_signatures,[]),code=[],_name=name||"",_args=getArgs(maxParams(_signatures));code.push("function "+_name+"("+_args.join(", ")+") {"),code.push(node.toCode(refs," ")),code.push("}");var factory=["(function ("+refs.name+") {",refs.toCode(),"return "+code.join("\n"),"})"].join("\n"),fn=eval(factory)(refs);return fn.signatures=mapSignatures(_signatures),fn}function maxParams(n){return n.reduce(function(n,r){return Math.max(n,r.params.length)},0)}function getTypeOf(n){for(var r in types)if(types.hasOwnProperty(r)&&"Object"!==r&&types[r](n))return r;return types.Object(n)?r:"unknown"}function contains(n,r){return-1!==n.indexOf(r)}Refs.prototype.add=function(n,r){var t=r||"fn";this.categories[t]||(this.categories[t]=[]);var e=this.categories[t].indexOf(n);return-1==e&&(e=this.categories[t].length,this.categories[t].push(n)),t+e},Refs.prototype.toCode=function(){var n=[],r=this.name+".categories",t=this.categories;return Object.keys(t).forEach(function(e){t[e].forEach(function(t,s){n.push("var "+e+s+" = "+r+"['"+e+"']["+s+"];")})}),n.join("\n")},Param.compare=function(n,r){if(n.anyType)return 1;if(n.anyType)return-1;if(contains(n.types,"Object"))return 1;if(contains(r.types,"Object"))return-1;if(n.hasConversions()){if(r.hasConversions()){var t=n.conversions.filter(function(n){return void 0!==n})[0],e=r.conversions.filter(function(n){return void 0!==n})[0];return typed.conversions.indexOf(t)-typed.conversions.indexOf(e)}return 1}return r.hasConversions()?-1:0},Param.prototype.overlapping=function(n){return this.types.some(function(r){return contains(n.types,r)})},Param.prototype.clone=function(){var n=new Param(this.types.slice(),this.varArgs);return n.conversions=this.conversions.slice(),n},Param.prototype.hasConversions=function(){return this.conversions.length>0},Param.prototype.toString=function(n){var r=this.types.map(function(r,t){var e=this.conversions[t];return n&&e?e.to:r}.bind(this)).filter(function(n,r,t){return t.indexOf(n)===r});return(this.varArgs?"...":"")+r.join("|")},Signature.prototype.clone=function(){return new Signature(this.params.slice(),this.fn)},Signature.prototype.expand=function(){function n(t,e){if(e.length<t.params.length){var s=t.params[e.length];if(s.varArgs){var i=s.clone();typed.conversions.forEach(function(n){if(!contains(s.types,n.from)&&contains(s.types,n.to)){var r=i.types.length;i.types[r]=n.from,i.conversions[r]=n}}),n(t,e.concat(i))}else s.types.forEach(function(r){n(t,e.concat(new Param(r)))}),typed.conversions.forEach(function(r){if(!contains(s.types,r.from)&&contains(s.types,r.to)){var i=new Param(r.from);i.conversions[0]=r,n(t,e.concat(i))}})}else r.push(new Signature(e,t.fn))}var r=[];return n(this,[]),r},Signature.compare=function(n,r){if(n.params.length>r.params.length)return 1;if(n.params.length<r.params.length)return-1;for(var t=0;t<n.params.length;t++){var e=Param.compare(n.params[t],r.params[t]);if(0!==e)return e}return 0},Signature.prototype.hasConversions=function(){return this.params.some(function(n){return n.hasConversions()})},Signature.prototype.toCode=function(n,r){var t=[],e=this.params.map(function(r,t){var e=r.conversions[0];return r.varArgs?"varArgs":e?n.add(e.convert,"convert")+"(arg"+t+")":"arg"+t}).join(", "),s=this.fn?n.add(this.fn,"signature"):void 0;return s?r+"return "+s+"("+e+"); // signature: "+this.params.join(", "):t.join("\n")},Signature.prototype.toString=function(){return this.params.join(", ")},Node.prototype.toCode=function(n,r,t){var e=[];if(this.param){var s=this.path.length-1,i=this.param.conversions[0],a="// type: "+(i?i.from+" (convert to "+i.to+")":this.param);if(this.param.varArgs)if(this.param.anyType)e.push(r+"if (arguments.length > "+s+") {"),e.push(r+" var varArgs = [];"),e.push(r+" for (var i = "+s+"; i < arguments.length; i++) {"),e.push(r+" varArgs.push(arguments[i]);"),e.push(r+" }"),e.push(this.signature.toCode(n,r+" ")),e.push(r+"}");else{var o=function(r,t){return r.map(function(r){return n.add(getTypeTest(r),"test")+"("+t+")"}).join(" || ")}.bind(this),u=this.param.types,p=u.filter(function(n,r){return void 0===this.param.conversions[r]}.bind(this));e.push(r+"if ("+o(u,"arg"+s)+") { "+a),e.push(r+" var varArgs = [arg"+s+"];"),e.push(r+" for (var i = "+(s+1)+"; i < arguments.length; i++) {"),e.push(r+" if ("+o(p,"arguments[i]")+") {"),e.push(r+" varArgs.push(arguments[i]);"),u.forEach(function(t,s){var i=this.param.conversions[s];if(i){var a=n.add(getTypeTest(t),"test"),o=n.add(i.convert,"convert");e.push(r+" }"),e.push(r+" else if ("+a+"(arguments[i])) {"),e.push(r+" varArgs.push("+o+"(arguments[i]));")}}.bind(this)),e.push(r+" } else {"),e.push(r+" throw createError('', arguments, i, '"+u.join(",")+"');"),e.push(r+" }"),e.push(r+" }"),e.push(this.signature.toCode(n,r+" ")),e.push(r+"}")}else if(this.param.anyType)e.push(r+"// type: any"),e.push(this._innerCode(n,r,t));else{var c=this.param.types[0],f="any"!==c?n.add(getTypeTest(c),"test"):null;e.push(r+"if ("+f+"(arg"+s+")) { "+a),e.push(this._innerCode(n,r+" ",t)),e.push(r+"}")}}else e.push(this._innerCode(n,r,t));return e.join("\n")},Node.prototype._innerCode=function(n,r,t){var e=[];this.signature&&(e.push(r+"if (arguments.length === "+this.path.length+") {"),e.push(this.signature.toCode(n,r+" ")),e.push(r+"}"));var s=this.childs.filter(function(n){return n.param.anyType})[0];this.childs.forEach(function(t){e.push(t.toCode(n,r,s))}),t&&!this.param.anyType&&e.push(t.toCode(n,r,s));var i=this._exceptions(n,r);return i&&e.push(i),e.join("\n")},Node.prototype._exceptions=function(n,r){var t=this.path.length;if(0===this.childs.length)return[r+"if (arguments.length > "+t+") {",r+" throw createError('', arguments, "+t+")",r+"}"].join("\n");var e=this.childs.reduce(function(n,r){return r.param&&r.param.types.forEach(function(r){-1===n.indexOf(r)&&n.push(r)}),n},[]);return r+"throw createError('', arguments, "+t+", '"+e.join(",")+"');"};var types={"null":function(n){return null===n},undefined:function(n){return void 0===n},"boolean":function(n){return"boolean"==typeof n},number:function(n){return"number"==typeof n},string:function(n){return"string"==typeof n},"function":function(n){return"function"==typeof n},Array:function(n){return Array.isArray(n)},Date:function(n){return n instanceof Date},RegExp:function(n){return n instanceof RegExp},Object:function(n){return"object"==typeof n}},config={},conversions=[],typed={config:config,types:types,conversions:conversions};return typed=_typed("typed",{Object:function(n){return _typed(null,n)},"string, Object":_typed,"string, function":function(n,r){var t={};return t[n]=r,_typed(r.name||null,t)},"string, string, function":function(n,r,t){var e={};return e[r]=t,_typed(n,e)},"...function":function(n){var r="",t={};return n.forEach(function(n,e){var s;if("object"!=typeof n.signatures)throw s=new TypeError("Function is no typed-function (index: "+e+")"),s.data={index:e},s;for(var i in n.signatures)if(n.signatures.hasOwnProperty(i)){if(t.hasOwnProperty(i))throw s=new Error('Signature "'+i+'" is defined twice'),s.data={signature:i},s;t[i]=n.signatures[i]}if(""!=n.name)if(""==r)r=n.name;else if(r!=n.name)throw s=new Error("Function names do not match (expected: "+r+", actual: "+n.name+")"),s.data={actual:n.name,expected:r},s}),_typed(r,t)}}),typed.config=config,typed.types=types,typed.conversions=conversions,typed}); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
27
1857
94710
1