typed-function
Advanced tools
Comparing version 0.4.0 to 0.5.0
@@ -24,8 +24,9 @@ var typed = require('../typed-function'); | ||
try { | ||
fn1('hello', 'world'); | ||
fn2('hello', 'world'); | ||
} | ||
catch (err) { | ||
console.log(err.toString()); | ||
// outputs: TypeError: Unexpected type of argument. | ||
// Expected: number, actual: string, index: 0. | ||
console.log('Wrong input will throw an error:'); | ||
console.log(' ' + err.toString()); | ||
// outputs: TypeError: Unexpected type of argument. | ||
// Expected: number or boolean, actual: string, index: 1. | ||
} |
@@ -42,3 +42,2 @@ var typed = require('../typed-function'); | ||
'string, number': function (name, value) { | ||
// note that the toFixed function is only available on numbers | ||
return 'Name: ' + name + ', length: ' + name.length + ', value: ' + value.toFixed(3); | ||
@@ -45,0 +44,0 @@ } |
# History | ||
## 2015-01-07, version 0.5.0 | ||
- Implemented support for merging typed functions. | ||
- Typed functions inherit the name of the function in case of one signature. | ||
- Fixed a bug where a regular argument was not matched when there was a | ||
signature with variable arguments too. | ||
- Slightly changed the error messages. | ||
## 2014-12-17, version 0.4.0 | ||
@@ -4,0 +13,0 @@ |
{ | ||
"name": "typed-function", | ||
"version": "0.4.0", | ||
"version": "0.5.0", | ||
"description": "Type checking for JavaScript functions", | ||
@@ -5,0 +5,0 @@ "author": "Jos de Jong <wjosdejong@gmail.com> (https://github.com/josdejong)", |
115
README.md
# typed-function | ||
Type checking for JavaScript functions. | ||
Move type checking logic and type conversions outside of your function in a | ||
flexible, organized way. Automatically throw informative errors in case of | ||
wrong input arguments. | ||
Features: | ||
- Type-checking of input arguments. | ||
## Features | ||
typed-function has the following features: | ||
- Runtime type-checking of input arguments. | ||
- Automatic type conversion of arguments. | ||
- Compose typed functions with multiple signatures. | ||
- Supports union types, any type, variable arguments. | ||
- Supports union types, any type, and variable arguments. | ||
- Detailed error messaging. | ||
@@ -16,2 +21,47 @@ | ||
## Why? | ||
In JavaScript, functions can be called with any number and any type of arguments. | ||
When writing a function, the easiest way is to just assume that the function | ||
will be called with the correct input. This leaves the function's behavior on | ||
invalid input undefined. The function may throw some error, or worse, | ||
it may silently fail or return wrong results. Typical errors are | ||
*TypeError: undefined is not a function* or *TypeError: Cannot call method | ||
'request' of undefined*. These error messages are not very helpful. It can be | ||
hard to debug them, as they can be the result of a series of nested function | ||
calls manipulating and propagating invalid or incomplete data. | ||
Often, JavaScript developers add some basic type checking where it is important, | ||
using checks like `typeof fn === 'function'`, `date instanceof Date`, and | ||
`Array.isArray(arr)`. For functions supporting multiple signatures, | ||
the type checking logic can grow quite a bit, and distract from the actual | ||
logic of the function. | ||
For functions dealing with a considerable amount of type checking and conversion | ||
logic, or functions facing a public API, it can be very useful to use the | ||
`typed-function` module to handle the type-checking logic. This way: | ||
- Users of the function get useful and consistent error messages when using | ||
the function wrongly. | ||
- The function cannot silently fail or silently give wrong results due to | ||
invalid input. | ||
- Correct type of input is assured inside the function. The function's code | ||
becomes easier to understand as it only contains the actual function logic. | ||
Lower level utility functions called by the type-checked function can | ||
possibly be kept simpler as they don't need to do additional type checking. | ||
It's important however not to *overuse* type checking: | ||
- Locking down the type of input that a function accepts can unnecessary limit | ||
it's flexibility. Keep functions as flexible and forgiving as possible, | ||
follow the | ||
[robustness principle](http://en.wikipedia.org/wiki/Robustness_principle) | ||
here: "be liberal in what you accept and conservative in what you send" | ||
(Postel's law). | ||
- There is no need to apply type checking to *all* functions. It may be | ||
enough to apply type checking to one tier of public facing functions. | ||
- There is a performance penalty involved for all type checking, so applying | ||
it everywhere can unnecessarily worsen the performance. | ||
## Load | ||
@@ -26,3 +76,4 @@ | ||
Example usage: | ||
Here some usage examples. More examples are available in the | ||
[/examples](/examples) folder. | ||
@@ -66,8 +117,8 @@ ```js | ||
try { | ||
fn4('hello world'); | ||
fn2('hello', 'world'); | ||
} | ||
catch (err) { | ||
console.log(err.toString()); | ||
// outputs: TypeError: Unexpected type of argument. | ||
// Expected: number, actual: string, index: 0. | ||
// outputs: TypeError: Unexpected type of argument. | ||
// Expected: number or boolean, actual: string, index: 1. | ||
} | ||
@@ -93,5 +144,5 @@ ``` | ||
- Multiple parameters: `string, number, function` | ||
- Multiple arguments: `string, number, function` | ||
- Union types: `number | string` | ||
- Variable parameters: `...number` | ||
- Variable arguments: `...number` | ||
- Any type: `any` | ||
@@ -104,12 +155,22 @@ | ||
A typed function can be constructed as: | ||
A typed function can be constructed in three ways: | ||
```js | ||
typed(signature: string, fn: function) : function | ||
typed(name: string, signature: string, fn: function) : function | ||
- With a single signature: | ||
``` | ||
typed(signature: string, fn: function) : function | ||
typed(name: string, signature: string, fn: function) : function | ||
``` | ||
typed(signatures: Object.<string, function>) : function | ||
typed(name: string, signatures: Object.<string, function>) : function | ||
``` | ||
- With multiple signatures: | ||
``` | ||
typed(signatures: Object.<string, function>) : function | ||
typed(name: string, signatures: Object.<string, function>) : function | ||
``` | ||
- Merge multiple typed functions into a new typed function | ||
``` | ||
typed(functions: ...function) : function | ||
``` | ||
### Properties | ||
@@ -165,13 +226,5 @@ | ||
signatures as key and the original sub-functions as value. | ||
- A property `name` containing name of the typed function or an empty string. | ||
## Performance | ||
Type checking input arguments adds some overhead to a function. For very small | ||
functions this overhead can be larger than the function execution itself is, | ||
but for any non-trivial function the overhead is typically small to neglectable. | ||
You need to keep in mind though that you probably would have to do the type | ||
checking done by `typed-function` anyway. | ||
## Roadmap | ||
@@ -181,10 +234,9 @@ | ||
- Be able to turn off exception throwing. | ||
- Extend function signatures: | ||
- Optional arguments like `'[number], array'` or like `number=, array` | ||
- Nullable arguments like `'?Object'` | ||
- Be able to merge typed functions into a new typed function, like | ||
`fn1 = merged(fn2, fn3)`. | ||
- Create a good benchmark, to get insight in the overhead. | ||
- Allow conversions not to be able to convert any input (for example string to | ||
number is not always possible). | ||
- Allow conversions to fail (for example string to number is not always | ||
possible). Call this `fallible` or `optional`? | ||
@@ -198,3 +250,6 @@ ### Version 2 | ||
- Array definitions like `'Array.<Person>'` | ||
- Improve performance of both generating a typed function as well as | ||
the performance and memory footprint of a typed function. | ||
## Test | ||
@@ -201,0 +256,0 @@ |
@@ -24,2 +24,20 @@ // test parse | ||
it('should create an unnamed function', function() { | ||
var fn = typed('string', function (str) { | ||
return 'foo'; | ||
}); | ||
assert.equal(fn('bar'), 'foo'); | ||
assert.equal(fn.name, ''); | ||
}); | ||
it('should inherit the name of the function implementation', function() { | ||
var fn = typed('string', function fn1 (str) { | ||
return 'foo'; | ||
}); | ||
assert.equal(fn('bar'), 'foo'); | ||
assert.equal(fn.name, 'fn1'); | ||
}); | ||
it('should compose a function with zero arguments', function() { | ||
@@ -88,3 +106,3 @@ var signatures = { | ||
assert.throws(function () {fn(new Date())}, /TypeError: Unexpected type of argument. Expected: number, actual: Date, index: 0./); | ||
assert.throws(function () {fn(new Date())}, /TypeError: Unexpected type of argument \(expected: number, actual: Date, index: 0\)/); | ||
}); | ||
@@ -99,3 +117,3 @@ | ||
assert.throws(function () {fn(1, 2)}, /TypeError: Too many arguments. Expected: 1, actual: 2./); | ||
assert.throws(function () {fn(1, 2)}, /TypeError: Too many arguments \(expected: 1, actual: 2\)/); | ||
}); | ||
@@ -173,3 +191,3 @@ | ||
assert.equal(fn(2), 'number'); | ||
assert.throws(function () {fn('string')}, /TypeError: Unexpected type of argument. Expected: number or boolean, actual: string, index: 0./); | ||
assert.throws(function () {fn('string')}, /TypeError: Unexpected type of argument \(expected: number or boolean, actual: string, index: 0\)/); | ||
}); | ||
@@ -193,7 +211,7 @@ | ||
assert.equal(sum(2,3,4), 9); | ||
assert.throws(function () {sum()}, /TypeError: Too few arguments. Expected: number, index: 0./); | ||
assert.throws(function () {sum(true)}, /TypeError: Unexpected type of argument. Expected: number, actual: boolean, index: 0./); | ||
assert.throws(function () {sum('string')}, /TypeError: Unexpected type of argument. Expected: number, actual: string, index: 0./); | ||
assert.throws(function () {sum(2, 'string')}, /TypeError: Unexpected type of argument. Expected: number, actual: string, index: 1./); | ||
assert.throws(function () {sum(2, 3, 'string')}, /TypeError: Unexpected type of argument. Expected: number, actual: string, index: 2./); | ||
assert.throws(function () {sum()}, /TypeError: Too few arguments \(expected: number, index: 0\)/); | ||
assert.throws(function () {sum(true)}, /TypeError: Unexpected type of argument \(expected: number, actual: boolean, index: 0\)/); | ||
assert.throws(function () {sum('string')}, /TypeError: Unexpected type of argument \(expected: number, actual: string, index: 0\)/); | ||
assert.throws(function () {sum(2, 'string')}, /TypeError: Unexpected type of argument \(expected: number, actual: string, index: 1\)/); | ||
assert.throws(function () {sum(2, 3, 'string')}, /TypeError: Unexpected type of argument \(expected: number, actual: string, index: 2\)/); | ||
}); | ||
@@ -210,5 +228,5 @@ | ||
assert.equal(fn('foo', 2, 4), 'foo: 2, 4'); | ||
assert.throws(function () {fn(2, 4)}, /TypeError: Unexpected type of argument. Expected: string, actual: number, index: 0./); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments. Expected: number, index: 1./); | ||
assert.throws(function () {fn('string', 'string')}, /TypeError: Unexpected type of argument. Expected: number, actual: string, index: 1./); | ||
assert.throws(function () {fn(2, 4)}, /TypeError: Unexpected type of argument \(expected: string, actual: number, index: 0\)/); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments \(expected: number, index: 1\)/); | ||
assert.throws(function () {fn('string', 'string')}, /TypeError: Unexpected type of argument \(expected: number, actual: string, index: 1\)/); | ||
}); | ||
@@ -226,4 +244,4 @@ | ||
assert.equal(fn('foo', 'bar'), 'foo: bar'); | ||
assert.throws(function () {fn(2, 4)}, /TypeError: Unexpected type of argument. Expected: string, actual: number, index: 0./); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments. Expected: any, index: 1./); | ||
assert.throws(function () {fn(2, 4)}, /TypeError: Unexpected type of argument \(expected: string, actual: number, index: 0\)/); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments \(expected: any, index: 1\)/); | ||
}); | ||
@@ -241,4 +259,4 @@ | ||
assert.equal(fn('foo', 'bar'), 'foo: bar'); | ||
assert.throws(function () {fn(2, 4)}, /TypeError: Unexpected type of argument. Expected: string, actual: number, index: 0./); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments. Expected: any, index: 1./); | ||
assert.throws(function () {fn(2, 4)}, /TypeError: Unexpected type of argument \(expected: string, actual: number, index: 0\)/); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments \(expected: any, index: 1\)/); | ||
}); | ||
@@ -255,4 +273,4 @@ | ||
assert.equal(fn(null, 2, 4), 'null: 2, 4'); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments. Expected: number, index: 1./); | ||
assert.throws(function () {fn('string', 'string')}, /TypeError: Unexpected type of argument. Expected: number, actual: string, index: 1./); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments \(expected: number, index: 1\)/); | ||
assert.throws(function () {fn('string', 'string')}, /TypeError: Unexpected type of argument \(expected: number, actual: string, index: 1\)/); | ||
}); | ||
@@ -269,6 +287,6 @@ | ||
strictEqualArray(fn('a',2,'c',3), ['a',2,'c',3]); | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments. Expected: number or string, index: 0./); | ||
assert.throws(function () {fn('string', true)}, /TypeError: Unexpected type of argument. Index: 1. Expected: string | number/); | ||
assert.throws(function () {fn(2, false)}, /TypeError: Unexpected type of argument. Index: 1. Expected: string | number/); | ||
assert.throws(function () {fn(2, 3, false)}, /TypeError: Unexpected type of argument. Index: 2. Expected: string | number/); | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments \(expected: number or string, index: 0\)/); | ||
assert.throws(function () {fn('string', true)}, /TypeError: Unexpected type of argument. Index: 1 \(expected: string | number/); | ||
assert.throws(function () {fn(2, false)}, /TypeError: Unexpected type of argument. Index: 1 \(expected: string | number/); | ||
assert.throws(function () {fn(2, 3, false)}, /TypeError: Unexpected type of argument. Index: 2 \(expected: string | number/); | ||
}); | ||
@@ -294,7 +312,23 @@ | ||
// FIXME: error should be Expected: string or boolean | ||
assert.throws(function () {fn(2, 4)}, /TypeError: Unexpected type of argument. Expected: boolean, actual: number, index: 0./); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments. Expected: number, index: 1./); | ||
assert.throws(function () {fn('string', true)}, /TypeError: Unexpected type of argument. Expected: number, actual: boolean, index: 1./); | ||
assert.throws(function () {fn(2, 4)}, /TypeError: Unexpected type of argument \(expected: string or boolean, actual: number, index: 0\)/); | ||
assert.throws(function () {fn('string')}, /TypeError: Too few arguments \(expected: number, index: 1\)/); | ||
assert.throws(function () {fn('string', true)}, /TypeError: Unexpected type of argument \(expected: number, actual: boolean, index: 1\)/); | ||
}); | ||
it('should continue with other options if varArgs do not match', function() { | ||
var fn = typed({ | ||
'...number': function (values) { | ||
return '...number'; | ||
}, | ||
'Object': function (value) { | ||
return 'Object'; | ||
} | ||
}); | ||
assert.equal(fn(2, 3), '...number'); | ||
assert.equal(fn(2), '...number'); | ||
assert.equal(fn({}), 'Object'); | ||
}); | ||
it('should throw an error in case of unexpected variable arguments', function() { | ||
@@ -323,8 +357,8 @@ assert.throws(function () { | ||
// FIXME: should return correct error message | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments. Expected: string or number or boolean, index: 0./); | ||
assert.throws(function () {fn(1,2,3)}, /TypeError: Unexpected type of argument. Expected: boolean, actual: number, index: 1./); | ||
assert.throws(function () {fn('str', 2)}, /TypeError: Unexpected type of argument. Expected: boolean, actual: number, index: 1./); | ||
assert.throws(function () {fn(true, 'str')},/TypeError: Unexpected type of argument. Expected: boolean or number, actual: string, index: 1./); | ||
assert.throws(function () {fn(2, 3)}, /TypeError: Unexpected type of argument. Expected: boolean, actual: number, index: 1./); | ||
assert.throws(function () {fn(2, 'str')}, /TypeError: Unexpected type of argument. Expected: boolean, actual: string, index: 1./); | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments \(expected: string or number or boolean, index: 0\)/); | ||
assert.throws(function () {fn(1,2,3)}, /TypeError: Unexpected type of argument \(expected: boolean, actual: number, index: 1\)/); | ||
assert.throws(function () {fn('str', 2)}, /TypeError: Unexpected type of argument \(expected: boolean, actual: number, index: 1\)/); | ||
assert.throws(function () {fn(true, 'str')},/TypeError: Unexpected type of argument \(expected: boolean or number, actual: string, index: 1\)/); | ||
assert.throws(function () {fn(2, 3)}, /TypeError: Unexpected type of argument \(expected: boolean, actual: number, index: 1\)/); | ||
assert.throws(function () {fn(2, 'str')}, /TypeError: Unexpected type of argument \(expected: boolean, actual: string, index: 1\)/); | ||
}); | ||
@@ -421,5 +455,5 @@ | ||
assert.equal(fn(2,'foo'), 'any,string'); | ||
assert.throws(function () {fn([], new Date())}, /TypeError: Unexpected type of argument. Expected: boolean or string, actual: Date, index: 1./); | ||
assert.throws(function () {fn(2, 2)}, /TypeError: Unexpected type of argument. Expected: boolean or string, actual: number, index: 1./); | ||
assert.throws(function () {fn(2)}, /TypeError: Too few arguments. Expected: boolean or string, index: 1./); | ||
assert.throws(function () {fn([], new Date())}, /TypeError: Unexpected type of argument \(expected: boolean or string, actual: Date, index: 1\)/); | ||
assert.throws(function () {fn(2, 2)}, /TypeError: Unexpected type of argument \(expected: boolean or string, actual: number, index: 1\)/); | ||
assert.throws(function () {fn(2)}, /TypeError: Too few arguments \(expected: boolean or string, index: 1\)/); | ||
}); | ||
@@ -445,4 +479,4 @@ | ||
assert.equal(fn('foo', 2), 'string,any'); | ||
assert.throws(function () {fn([], new Date())}, /TypeError: Unexpected type of argument. Expected: boolean or number, actual: Date, index: 1./); | ||
assert.throws(function () {fn([], 'foo')}, /TypeError: Unexpected type of argument. Expected: boolean or number, actual: string, index: 1./) | ||
assert.throws(function () {fn([], new Date())}, /TypeError: Unexpected type of argument \(expected: boolean or number, actual: Date, index: 1\)/); | ||
assert.throws(function () {fn([], 'foo')}, /TypeError: Unexpected type of argument \(expected: boolean or number, actual: string, index: 1\)/) | ||
}); | ||
@@ -465,3 +499,3 @@ | ||
assert.equal(fn([]), 'any'); | ||
assert.throws(function () {fn([], 'foo')}, /TypeError: Too many arguments. Expected: 1, actual: 2./) | ||
assert.throws(function () {fn([], 'foo')}, /TypeError: Too many arguments \(expected: 1, actual: 2\)/) | ||
}); | ||
@@ -477,3 +511,12 @@ | ||
{from: 'boolean', to: 'string', convert: function (x) {return x + '';}}, | ||
{from: 'number', to: 'string', convert: function (x) {return x + '';}} | ||
{from: 'number', to: 'string', convert: function (x) {return x + '';}}, | ||
{ | ||
from: 'string', | ||
to: 'Date', | ||
convert: function (x) { | ||
var d = new Date(x); | ||
return isNaN(d.valueOf()) ? undefined : d; | ||
}, | ||
fallible: true | ||
} | ||
]; | ||
@@ -608,3 +651,3 @@ }); | ||
assert.throws(function () {fn(new Date(), '2')}, /TypeError: Unexpected type of argument. Expected: string or number, actual: Date, index: 0./) | ||
assert.throws(function () {fn(new Date(), '2')}, /TypeError: Unexpected type of argument \(expected: string or number, actual: Date, index: 0\)/) | ||
}); | ||
@@ -631,3 +674,2 @@ | ||
it('should add non-conflicting conversions to a function with one argument', function() { | ||
@@ -643,6 +685,76 @@ var fn = typed({ | ||
assert.equal(fn(true), 1); | ||
}); | ||
it.skip('should create a fallible conversion', function() { | ||
typed.config.minify = false; // TODO: cleanup | ||
var fn = typed({ | ||
'Date': function (a) { | ||
return a; | ||
} | ||
}); | ||
console.log('FN', fn.toString()); // TODO: cleanup | ||
var d = new Date(); | ||
assert.equal(fn(d), d); | ||
assert.equal(fn('2015-01-04T20:00:00Z').toString(), new Date('2015-01-04T20:00:00Z').toString()); | ||
assert.throws(function () { | ||
fn('invalid date'); | ||
}, /TypeError: Unexpected type of argument \(expected: Date, actual: string, index: 0\)/); | ||
}); | ||
}); | ||
describe('merge', function () { | ||
it('should merge two typed-functions', function () { | ||
var typed1 = typed('boolean', function (value) { return 'boolean:' + value; }); | ||
var typed2 = typed('number', function (value) { return 'number:' + value; }); | ||
var typed3 = typed(typed1, typed2); | ||
assert.deepEqual(Object.keys(typed3.signatures).sort(), ['boolean', 'number']); | ||
assert.strictEqual(typed3(true), 'boolean:true'); | ||
assert.strictEqual(typed3(2), 'number:2'); | ||
assert.throws(function () {typed3('foo')}, /TypeError: Unexpected type of argument \(expected: boolean or number, actual: string, index: 0\)/); | ||
}); | ||
it('should merge three typed-functions', function () { | ||
var typed1 = typed('boolean', function (value) { return 'boolean:' + value; }); | ||
var typed2 = typed('number', function (value) { return 'number:' + value; }); | ||
var typed3 = typed('string', function (value) { return 'string:' + value; }); | ||
var typed4 = typed(typed1, typed2, typed3); | ||
assert.deepEqual(Object.keys(typed4.signatures).sort(), ['boolean', 'number', 'string']); | ||
assert.strictEqual(typed4(true), 'boolean:true'); | ||
assert.strictEqual(typed4(2), 'number:2'); | ||
assert.strictEqual(typed4('foo'), 'string:foo'); | ||
assert.throws(function () {typed4(new Date())}, /TypeError: Unexpected type of argument \(expected: boolean or number or string, actual: Date, index: 0\)/); | ||
}); | ||
it('should throw an error in case of conflicting signatures when merging', function () { | ||
var typed1 = typed('boolean', function (value) { return 'boolean:' + value; }); | ||
var typed2 = typed('boolean', function (value) { return 'boolean:' + value; }); | ||
assert.throws(function () { | ||
typed(typed1, typed2) | ||
}, /Error: Conflicting signatures: "boolean" is defined twice/); | ||
}); | ||
it('should throw an error in case of conflicting names when merging', function () { | ||
var typed1 = typed('fn1', 'boolean', function () {}); | ||
var typed2 = typed('fn2', 'string', function () {}); | ||
var typed3 = typed('number', function () {}); | ||
assert.throws(function () { | ||
typed(typed1, typed2) | ||
}, /Error: Function names do not match \(expected: fn1, actual: fn2\)/); | ||
var typed4 = typed(typed2, typed3); | ||
assert.equal(typed4.name, 'fn2'); | ||
}); | ||
}); | ||
describe('errors', function () { | ||
@@ -652,4 +764,4 @@ it('should give correct error in case of too few arguments', function() { | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments. Expected: string, index: 0./); | ||
assert.throws(function () {fn('foo')}, /TypeError: Too few arguments. Expected: boolean, index: 1./); | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments \(expected: string, index: 0\)/); | ||
assert.throws(function () {fn('foo')}, /TypeError: Too few arguments \(expected: boolean, index: 1\)/); | ||
}); | ||
@@ -660,3 +772,3 @@ | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments. Expected: string, index: 0./); | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments \(expected: string, index: 0\)/); | ||
}); | ||
@@ -667,4 +779,4 @@ | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments. Expected: boolean, index: 0./); | ||
assert.throws(function () {fn(true)}, /TypeError: Too few arguments. Expected: string, index: 1./); | ||
assert.throws(function () {fn()}, /TypeError: Too few arguments \(expected: boolean, index: 0\)/); | ||
assert.throws(function () {fn(true)}, /TypeError: Too few arguments \(expected: string, index: 1\)/); | ||
}); | ||
@@ -675,4 +787,4 @@ | ||
assert.throws(function () {fn('foo', true, 2)}, /TypeError: Too many arguments. Expected: 2, actual: 3./); | ||
assert.throws(function () {fn('foo', true, 2, 1)}, /TypeError: Too many arguments. Expected: 2, actual: 4./); | ||
assert.throws(function () {fn('foo', true, 2)}, /TypeError: Too many arguments \(expected: 2, actual: 3\)/); | ||
assert.throws(function () {fn('foo', true, 2, 1)}, /TypeError: Too many arguments \(expected: 2, actual: 4\)/); | ||
}); | ||
@@ -683,3 +795,3 @@ | ||
assert.throws(function () {fn('foo')}, /TypeError: Unexpected type of argument. Expected: boolean, actual: string, index: 0./); | ||
assert.throws(function () {fn('foo')}, /TypeError: Unexpected type of argument \(expected: boolean, actual: string, index: 0\)/); | ||
}); | ||
@@ -690,3 +802,3 @@ | ||
assert.throws(function () {fn(2)}, /TypeError: Unexpected type of argument. Expected: boolean or string or Date, actual: number, index: 0./); | ||
assert.throws(function () {fn(2)}, /TypeError: Unexpected type of argument \(expected: boolean or string or Date, actual: number, index: 0\)/); | ||
}); | ||
@@ -697,5 +809,5 @@ | ||
assert.throws(function () {fn(true)}, /TypeError: Unexpected type of argument. Expected: number, actual: boolean, index: 0./); | ||
assert.throws(function () {fn(2, true)}, /TypeError: Unexpected type of argument. Expected: number, actual: boolean, index: 1./); | ||
assert.throws(function () {fn(2, 3, true)}, /TypeError: Unexpected type of argument. Expected: number, actual: boolean, index: 2./); | ||
assert.throws(function () {fn(true)}, /TypeError: Unexpected type of argument \(expected: number, actual: boolean, index: 0\)/); | ||
assert.throws(function () {fn(2, true)}, /TypeError: Unexpected type of argument \(expected: number, actual: boolean, index: 1\)/); | ||
assert.throws(function () {fn(2, 3, true)}, /TypeError: Unexpected type of argument \(expected: number, actual: boolean, index: 2\)/); | ||
}); | ||
@@ -706,6 +818,6 @@ | ||
assert.throws(function () {fn(true)}, /TypeError: Unexpected type of argument. Expected: string, actual: boolean, index: 0./); | ||
assert.throws(function () {fn('foo', true)}, /TypeError: Unexpected type of argument. Expected: number, actual: boolean, index: 1./); | ||
assert.throws(function () {fn('foo', 2, true)}, /TypeError: Unexpected type of argument. Expected: number, actual: boolean, index: 2./); | ||
assert.throws(function () {fn('foo', 2, 3, true)}, /TypeError: Unexpected type of argument. Expected: number, actual: boolean, index: 3./); | ||
assert.throws(function () {fn(true)}, /TypeError: Unexpected type of argument \(expected: string, actual: boolean, index: 0\)/); | ||
assert.throws(function () {fn('foo', true)}, /TypeError: Unexpected type of argument \(expected: number, actual: boolean, index: 1\)/); | ||
assert.throws(function () {fn('foo', 2, true)}, /TypeError: Unexpected type of argument \(expected: number, actual: boolean, index: 2\)/); | ||
assert.throws(function () {fn('foo', 2, 3, true)}, /TypeError: Unexpected type of argument \(expected: number, actual: boolean, index: 3\)/); | ||
}); | ||
@@ -716,5 +828,5 @@ | ||
assert.throws(function () {fn('foo')}, /TypeError: Unexpected type of argument. Expected: number or boolean, actual: string, index: 0./); | ||
assert.throws(function () {fn(2, 'foo')}, /TypeError: Unexpected type of argument. Expected: number or boolean, actual: string, index: 1./); | ||
assert.throws(function () {fn(2, true, 'foo')}, /TypeError: Unexpected type of argument. Expected: number or boolean, actual: string, index: 2./); | ||
assert.throws(function () {fn('foo')}, /TypeError: Unexpected type of argument \(expected: number or boolean, actual: string, index: 0\)/); | ||
assert.throws(function () {fn(2, 'foo')}, /TypeError: Unexpected type of argument \(expected: number or boolean, actual: string, index: 1\)/); | ||
assert.throws(function () {fn(2, true, 'foo')}, /TypeError: Unexpected type of argument \(expected: number or boolean, actual: string, index: 2\)/); | ||
}); | ||
@@ -721,0 +833,0 @@ }); |
@@ -407,8 +407,8 @@ /** | ||
var err = params.refs.add(unexpectedType, 'err'); | ||
code.push(prefix + ' throw ' + err + '(\'' + this.type.types + '\', arguments[i], i); // Unexpected type'); | ||
code.push(prefix + ' if (i > ' + (args.length - 1) + ') {'); | ||
code.push(prefix + ' throw ' + err + '(\'' + this.type.types + '\', arguments[i], i); // Unexpected type'); | ||
code.push(prefix + ' }'); | ||
} | ||
else { | ||
code.push(prefix + ' match = false;'); | ||
code.push(prefix + ' break;'); | ||
} | ||
code.push(prefix + ' match = false;'); | ||
code.push(prefix + ' break;'); | ||
@@ -505,3 +505,3 @@ code.push(prefix + ' }'); | ||
var actualType = getTypeOf(actual); | ||
var err = new TypeError(message + '. Expected: ' + arr.join(' or ') + ', actual: ' + actualType + ', index: ' + index + '.'); | ||
var err = new TypeError(message + ' (expected: ' + arr.join(' or ') + ', actual: ' + actualType + ', index: ' + index + ')'); | ||
err.data = { | ||
@@ -525,3 +525,3 @@ message: message, | ||
var message = 'Too few arguments'; | ||
var err = new TypeError(message + '. Expected: ' + arr.join(' or ') + ', index: ' + index + '.'); | ||
var err = new TypeError(message + ' (expected: ' + arr.join(' or ') + ', index: ' + index + ')'); | ||
err.data = { | ||
@@ -543,3 +543,3 @@ message: message, | ||
var message = 'Too many arguments'; | ||
var err = new TypeError(message + '. Expected: ' + expected + ', actual: ' + actual + '.'); | ||
var err = new TypeError(message + ' (expected: ' + expected + ', actual: ' + actual + ')'); | ||
err.data = { | ||
@@ -581,3 +581,2 @@ message: message, | ||
var arg = 'arg' + params.args.length; | ||
var err; | ||
@@ -588,4 +587,10 @@ var types = Object.keys(this.childs); | ||
if (firstChild && firstChild.varArgs) { | ||
err = params.refs.add(tooFewArguments, 'err'); | ||
code.push(prefix + 'throw ' + err + '(\'' + firstChild.type.types.join(',') + '\', arguments.length); // Too few arguments'); | ||
// TODO: call firstChild._exceptions here? | ||
var errC = params.refs.add(tooFewArguments, 'err'); | ||
var errD = params.refs.add(unexpectedType, 'err'); | ||
code.push(prefix + 'if (arguments.length === ' + argCount + ') {'); | ||
code.push(prefix + ' throw ' + errC + '(\'' + firstChild.type.types.join(',') + '\', arguments.length); // Too few arguments'); | ||
code.push(prefix + '} else {'); | ||
code.push(prefix + ' throw ' + errD + '(\'' + firstChild.type.types.join(',') + '\', ' + arg + ', ' + argCount + '); // Unexpected type'); | ||
code.push(prefix + '}'); | ||
} | ||
@@ -595,3 +600,3 @@ else { | ||
code.push(prefix + 'if (arguments.length > ' + argCount + ') {'); | ||
err = params.refs.add(tooManyArguments, 'err'); | ||
var err = params.refs.add(tooManyArguments, 'err'); | ||
code.push(prefix + ' throw ' + err + '(' + argCount + ', arguments.length); // Too many arguments'); | ||
@@ -603,9 +608,13 @@ code.push(prefix + '}'); | ||
var errA = params.refs.add(tooFewArguments, 'err'); | ||
var errB = params.refs.add(unexpectedType, 'err'); | ||
var typeNames = types.map(function (type) { | ||
return type.replace(/\.\.\./, ''); | ||
}); | ||
// TODO: add "Actual: ..." to the error message | ||
code.push(prefix + 'if (arguments.length === ' + argCount + ') {'); | ||
code.push(prefix + ' throw ' + errA + '(\'' + types.join(',') + '\', arguments.length); // Too few arguments'); | ||
code.push(prefix + ' throw ' + errA + '(\'' + typeNames.join(',') + '\', arguments.length); // Too few arguments'); | ||
code.push(prefix + '}'); | ||
var errB = params.refs.add(unexpectedType, 'err'); | ||
code.push(prefix + 'throw ' + errB + '(\'' + types.join(',') + '\', ' + arg + ', ' + argCount + '); // Unexpected type'); | ||
// TODO: add "Actual: ..." to the error message | ||
code.push(prefix + 'else {'); | ||
code.push(prefix + ' throw ' + errB + '(\'' + typeNames.join(',') + '\', ' + arg + ', ' + argCount + '); // Unexpected type'); | ||
code.push(prefix + '}'); | ||
} | ||
@@ -1022,3 +1031,3 @@ } | ||
signatures[signature] = fn; | ||
return _typed(null, signatures); | ||
return _typed(fn.name || null, signatures); | ||
}, | ||
@@ -1029,5 +1038,53 @@ 'string, string, function': function(name, signature, fn) { | ||
return _typed(name, signatures); | ||
}, | ||
'...function': function (fns) { | ||
var name = ''; | ||
var signatures = {}; | ||
fns.forEach(function (fn, index) { | ||
var err; | ||
// test whether this is a typed-function | ||
if (!(typeof fn.signatures === 'object')) { | ||
err = new TypeError('Function is no typed-function (index: ' + index + ')'); | ||
err.data = {index: index}; | ||
throw err; | ||
} | ||
// merge the signatures | ||
for (var signature in fn.signatures) { | ||
if (fn.signatures.hasOwnProperty(signature)) { | ||
if (signatures.hasOwnProperty(signature)) { | ||
err = new Error('Conflicting signatures: "' + signature + '" is defined twice.'); | ||
err.data = {signature: signature}; | ||
throw err; | ||
} | ||
else { | ||
signatures[signature] = fn.signatures[signature]; | ||
} | ||
} | ||
} | ||
// merge function name | ||
if (fn.name != '') { | ||
if (name == '') { | ||
name = fn.name; | ||
} | ||
else if (name != fn.name) { | ||
err = new Error('Function names do not match (expected: ' + name + ', actual: ' + fn.name + ')'); | ||
err.data = { | ||
actual: fn.name, | ||
expected: name | ||
}; | ||
throw err; | ||
} | ||
} | ||
}); | ||
return _typed(name, signatures); | ||
} | ||
}); | ||
//console.log(typed.toString()) | ||
// attach types and conversions to the final `typed` function | ||
@@ -1034,0 +1091,0 @@ typed.config = config; |
@@ -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 compareTypes(e,t){return"any"===e?1:"any"===t?-1:"Object"===e?1:"Object"===t?-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){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('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=o}n.fn=e.fn,n.varArgs=e.varArgs}),r}function minify(e){return e.replace(/\/\/.*/g,"").replace(/\s*\n\s*/gm,"").replace(/\s?([{}()=<>;,]+)\s?/g,function(e,t){return t}).replace(/(signature|test|convert|arg)(?=\d)|varArgs|match/g,function(e){return e.charAt(0)})}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");typed.config.minify&&(factory=minify(factory));var 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=p.types.map(function(t){var r=e.refs.add(getTypeTest(t),"test");return r+"(arguments[i])"}).join(" || ");if(i.push(o+"if (arguments.length >= "+f.length+") {"),i.push(o+" var match = true;"),i.push(o+" var varArgs = [];"),i.push(o+" for (var i = "+(f.length-1)+"; i < arguments.length; i++) {"),i.push(o+" if ("+y+") { // 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 v=e.refs.add(unexpectedType,"err");i.push(o+" throw "+v+"('"+this.type.types+"', arguments[i], i); // Unexpected type")}else i.push(o+" match = false;"),i.push(o+" break;");i.push(o+" }"),i.push(o+" }"),i.push(o+" if (match) {"),i.push(o+" return "+a+"("+f.join(", ")+"); // signature: "+c.join(", ")),i.push(o+" }"),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;if(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){var s=this._conversionsToCode(e);s.length>0&&t.push(s)}return e.exceptions&&t.push(this._exceptions(e)),t.join("\n")},Node.prototype._exceptions=function(e){var t,r=[],n=e.prefix,s=e.args.length,i="arg"+e.args.length,o=Object.keys(this.childs),a=o.length>0?this.childs[o[0]]:void 0;if(a&&a.varArgs)t=e.refs.add(tooFewArguments,"err"),r.push(n+"throw "+t+"('"+a.type.types.join(",")+"', arguments.length); // Too few arguments");else if(0===o.length)r.push(n+"if (arguments.length > "+s+") {"),t=e.refs.add(tooManyArguments,"err"),r.push(n+" throw "+t+"("+s+", arguments.length); // Too many arguments"),r.push(n+"}");else if(-1===o.indexOf("any")){var u=e.refs.add(tooFewArguments,"err");r.push(n+"if (arguments.length === "+s+") {"),r.push(n+" throw "+u+"('"+o.join(",")+"', arguments.length); // Too few arguments"),r.push(n+"}");var p=e.refs.add(unexpectedType,"err");r.push(n+"throw "+p+"('"+o.join(",")+"', "+i+", "+s+"); // Unexpected type")}return r.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.join("\n")},Node.prototype._getChilds=function(){return Object.keys(this.childs).sort(compareTypes).map(function(e){return this.childs[e]}.bind(this))},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)||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 i?(t.push(" // exactly matching signatures"),this._getChilds().forEach(function(e){t.push(e._toCode(merge(a,{conversions:!1,exceptions:!1})))}),t.push(" // convert into matching signatures"),this._getChilds().forEach(function(e){t.push(e._toCode(merge(a,{conversions:!0,exceptions:!0})))}),t.push(this._conversionsToCode(merge(a,{conversions:!0,exceptions:!0})))):(t.push(" // exactly matching signatures"),this._getChilds().forEach(function(e){t.push(e._toCode(merge(a,{conversions:!1,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={minify:!0},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(null,r)},"string, string, function":function(e,t,r){var n={};return n[t]=r,_typed(e,n)}}),typed.config=config,typed.types=types,typed.conversions=conversions,typed}); | ||
!function(e,t){"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?module.exports=t():e.typed=t()}(this,function(){"use strict";function compareTypes(e,t){return"any"===e?1:"any"===t?-1:"Object"===e?1:"Object"===t?-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){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('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=o}n.fn=e.fn,n.varArgs=e.varArgs}),r}function minify(e){return e.replace(/\/\/.*/g,"").replace(/\s*\n\s*/gm,"").replace(/\s?([{}()=<>;,]+)\s?/g,function(e,t){return t}).replace(/(signature|test|convert|arg)(?=\d)|varArgs|match/g,function(e){return e.charAt(0)})}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");typed.config.minify&&(factory=minify(factory));var 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=p.types.map(function(t){var r=e.refs.add(getTypeTest(t),"test");return r+"(arguments[i])"}).join(" || ");if(i.push(o+"if (arguments.length >= "+f.length+") {"),i.push(o+" var match = true;"),i.push(o+" var varArgs = [];"),i.push(o+" for (var i = "+(f.length-1)+"; i < arguments.length; i++) {"),i.push(o+" if ("+y+") { // 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 v=e.refs.add(unexpectedType,"err");i.push(o+" if (i > "+(f.length-1)+") {"),i.push(o+" throw "+v+"('"+this.type.types+"', arguments[i], i); // Unexpected type"),i.push(o+" }")}i.push(o+" match = false;"),i.push(o+" break;"),i.push(o+" }"),i.push(o+" }"),i.push(o+" if (match) {"),i.push(o+" return "+a+"("+f.join(", ")+"); // signature: "+c.join(", ")),i.push(o+" }"),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;if(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){var s=this._conversionsToCode(e);s.length>0&&t.push(s)}return 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.length>0?this.childs[i[0]]:void 0;if(o&&o.varArgs){var a=e.refs.add(tooFewArguments,"err"),u=e.refs.add(unexpectedType,"err");t.push(r+"if (arguments.length === "+n+") {"),t.push(r+" throw "+a+"('"+o.type.types.join(",")+"', arguments.length); // Too few arguments"),t.push(r+"} else {"),t.push(r+" throw "+u+"('"+o.type.types.join(",")+"', "+s+", "+n+"); // Unexpected type"),t.push(r+"}")}else if(0===i.length){t.push(r+"if (arguments.length > "+n+") {");var p=e.refs.add(tooManyArguments,"err");t.push(r+" throw "+p+"("+n+", arguments.length); // Too many arguments"),t.push(r+"}")}else if(-1===i.indexOf("any")){var h=e.refs.add(tooFewArguments,"err"),f=e.refs.add(unexpectedType,"err"),c=i.map(function(e){return e.replace(/\.\.\./,"")});t.push(r+"if (arguments.length === "+n+") {"),t.push(r+" throw "+h+"('"+c.join(",")+"', arguments.length); // Too few arguments"),t.push(r+"}"),t.push(r+"else {"),t.push(r+" throw "+f+"('"+c.join(",")+"', "+s+", "+n+"); // Unexpected type"),t.push(r+"}")}return 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.join("\n")},Node.prototype._getChilds=function(){return Object.keys(this.childs).sort(compareTypes).map(function(e){return this.childs[e]}.bind(this))},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)||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 i?(t.push(" // exactly matching signatures"),this._getChilds().forEach(function(e){t.push(e._toCode(merge(a,{conversions:!1,exceptions:!1})))}),t.push(" // convert into matching signatures"),this._getChilds().forEach(function(e){t.push(e._toCode(merge(a,{conversions:!0,exceptions:!0})))}),t.push(this._conversionsToCode(merge(a,{conversions:!0,exceptions:!0})))):(t.push(" // exactly matching signatures"),this._getChilds().forEach(function(e){t.push(e._toCode(merge(a,{conversions:!1,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={minify:!0},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}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
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
98795
18
1870
259
0