introspect-typed
v1.0.0
Published
A small library to provide function type checks functionalities
Downloads
3
Readme
introspect-typed
Provides rudimentary type checking and function overloading in Javascript.
- Install
- API
Install
npm install introspect-typed
API
matchType
: Type checking individual values
Other methods (typeChecked
, overload
) are based on this functionality.
Direct match
var matchType = require('introspect-typed').matchType;
matchType(String, 'yes!'); // => true
matchType(Number, 'no...'); // => false
Getting a matcher function
var matchType = require('introspect-typed').matchType;
var stringMatcher = matchType(String);
stringMatcher('yes!'); // => true
stringMatcher(22.145); // => false
Custom types work as well
var matchType = require('introspect-typed').matchType;
function Custo () { this.type = 'custo'; }
var custoMatcher = matchType(Custo);
custoMatcher(new Custo()); // => true
custoMatcher({type: 'custo'}); // => false
Matching against any type
Use Typed.Any
if you want to match any value.
var Typed = require('introspect-typed');
var matchType = Typed.matchType;
matchType(Typed.Any, undefined); // => true
Matching against several types
Use Typed.Either
if you want to match several types.
var Typed = require('introspect-typed');
var matchType = Typed.matchType;
matchType(Typed.Either(String, Number), 'string'); // => true
matchType(Typed.Either(String, Number), 2); // => true
matchType(Typed.Either(String, Number), {}); // => false
Matching against a predicate
Use Typed.Matcher
if you want to match with a predicate.
var Typed = require('introspect-typed');
var matchType = Typed.matchType;
var type = Typed.Matcher(function (v) { return v.length === 2; });
matchType(type, {length: 2}); // => true
matchType(type, '12'); // => true
matchType(type, [1,2]); // => true
matchType(type, '1'); // => false
matchType(type, [1,2,3]); // => false
Matching against an ES6 iterable
Iterables are objects with a Symbol.iterator
property containing a function.
In ES6, strings, arrays, sets, maps and generator functions, among others, are
iterables.
Use Typed.Iterable
if you want to match es6 iterables.
var Typed = require('introspect-typed');
var Iterable = Typed.Iterable;
matchType(Iterable, []); // => true
matchType(Iterable, '12'); // => true
matchType(Iterable, {}); // => true
matchType(Iterable, 2); // => false
matchType(Iterable, null); // => false
typeChecked
: Type checking function calls
var Typed = require('introspect-typed');
var typeChecked = Typed.typeChecked;
var Either = Typed.Either;
function Custo () { this.type = 'custo'; }
Declare a type checked function by calling typeChecked
with
- an array of types,
- the function you want to type check.
var tcFn = typeChecked([Either(String, Number), Custo], function (s, c) {
return s + c;
});
tcFn('string', new Custo()); // => 'string[object Object]'
tcFn(Infinity, new Custo()); // => 'Infinity[object Object]'
Rest parameters
Check type for rest parameters by using Typed.Rest
. In ES6:
var Rest = Typed.Rest;
var typedFn = typeChecked([String, Number, Rest(Either(Custom, String))],
(s, n, ...cs) => ({ result: s.repeat(n), rest: cs.length });
In ES5:
var Rest = Typed.Rest;
var typedFn = typeChecked([String, Number, Rest(Either(Custom, String))],
function (s, n) {
var cs = [].slice.call(arguments, 2);
return { result: s.repeat(n), rest: cs.length };
});
Rest(String)
will match all strings at the end of the arguments.Rest(Any)
will match anything at the end of the arguments.Rest(Either(X,Y))
will match any number ofX
s andY
s at the end of the arguments.
Note that Rest(...)
must be the last element in the type array, otherwise
an error will be thrown while matching.
Error management
By default, the typechecked function throws a TypeError
when not given
proper arguments.
tcFn('string', {type:'custo'}); // => throws TypeError
You can set the error management behaviour yourself (at any time).
tcFn.onError(function (error, args, types) {
console.error(error);
});
And set back the original throwing behaviour by calling onError
with
a falsey value.
tcFn.onError(false);
overload
: Overloading functions
Chaining methods
When creating an API, we often want our methods to accept different kinds of inputs, but the code that results from these checks is ugly and provides many occasions for bugs.
Instead, using overload
, one can do the following:
var overload = require('introspect-typed').overload;
var createObj = overload(
// This method is the default method, called when no other case
// has matched
function (obj) {
obj.test = 1;
return obj;
}
)
.when('withName', // Here we define a 'withName' method in the overloading object
[String, Object], // which matches calls with a String and an Object
function (name, obj, o) // the last arg is the overloading object
{
obj.name = name;
// We can use the overloading object to call other methods defined
// for example the default method, passed to overload
return createObj.default(obj);
}
)
.when([String, String, Object], function (name, desc, obj, o) {
obj.desc = desc;
// Or we can use it to call named overloading methods.
return createObj.withName(name, obj);
});
Without the comments:
var overload = require('introspect-typed').overload;
var createObj = overload(function (obj) {
obj.test = 1;
return obj;
}).when('withName', [String, Object], function (name, obj, o) {
obj.name = name;
return createObj.default(obj);
}).when([String, String, Object], function (name, desc, obj, o) {
obj.desc = desc;
return createObj.withName(name, obj);
});
Calling a named overloaded method directly bypasses the type check.
To have a completely type checked method, one could do this instead:
var overload = require('introspect-typed').overload;
var typeChecked = require('introspect-typed').typeChecked;
var createObj = overload([Object], function (obj) {
obj.test = 1;
return obj;
})
.when([String, Object], function (name, obj, o) {
obj.name = name;
return createObj(obj);
})
.when([String, String, Object], function (name, desc, obj, o) {
obj.desc = desc;
return createObj(name, obj);
});
The calls to overload
with arguments matching [Array, Function]
are
equivalent to overload(typeChecked(types, fn))
.
Complete overloading
var overload = require('introspect-typed').overload;
var fn = overload(function () { return this; })
// give a number : increments it
.when([Number],
function (a) { return a + 1; })
// give two numbers : multiply them
.when([Number, Number],
function (n1, n2) { return n1 * n2; })
// give two strings: join them with '-'
.when([String, String],
function (s1, s2) { return s1 + '-' + s2; })
// give 3 strings: join them with '!'
.when([String, String, String],
function (s1, s2, s3) { return [s1,s2,s3].join('!'); })
// returns the length for a String or an Array
.when([Either(String,Array)],
function (v) { return v.length; })
// logs arguments when there are 4 of them
.when([Any, Any, Any, Any],
function () { console.log(arguments); return arguments[3]; })
// give a number and a string: repeat the string n times
.when([Number, String],
function (n, s) { return s.repeat(n); });
Calling this method with different kinds of arguments changes its behaviour.
expect(fn('2','57', 'x')).to.be.equal('2!57!x');
expect(fn('2','57')).to.be.equal('2-57');
expect(fn(2,'57')).to.be.equal('5757');
expect(fn(2,57)).to.be.equal(2*57);
expect(fn(42)).to.be.equal(43);
The this
value is conserved.
expect(fn.apply(42, [])).to.be.equal(42);
build
The build
function allows to create a new Typed context.
This allows to modify the matchType
function, to pass more cases to the
type checking.
var build = require('introspect-typed').build;
var newTypedContext = build();
var matchType = newTypedContext.matchType;
var Truthy = {};
expect(matchType(Truthy, 1)).to.be.false;
expect(matchType(Truthy, null)).to.be.false;
matchType.addTypeMatchCase({
case: function (type) { return type === Truthy; },
match: function (type) { return function (val) { return !!val }; }
});
expect(matchType(Truthy, 1)).to.be.true;
expect(matchType(Truthy, null)).to.be.false;