tcomb is a library for Node.js and the browser which allows you to check the types of JavaScript values at runtime with a simple syntax. It is great for checking external input, for testing and for adding safety to your internal code.
Some features include:
- write complex domain models in a breeze and with small code footprint
- easy debugging
- instances are immutables by default
- encode/decode of domain models to/from JSON for free
The library provides a built-in assert
function, if an assert fails the debugger kicks in
so you can inspect the stack and quickly find out what's wrong.
You can handle:
JavaScript native types
- Nil:
null
andundefined
- Str: strings
- Num: numbers
- Bool: booleans
- Arr: arrays
- Obj: plain objects
- Func: functions
- Err: errors
type combinators (build new types from those already defined)
- struct (i.e. classes)
- union
- maybe
- enums
- tuple
- subtype
- list
- function type (experimental)
Let's build a product model
var Product = struct({
name: Str, // required string
desc: maybe(Str), // optional string, can be null
home: Url, // a subtype of a string
shippings: list(Str), // a list of shipping methods
category: Category, // enum, one of [audio, video]
price: union(Num, Price), // a price (dollars) OR in another currency
dim: tuple([Num, Num]) // dimensions (width, height)
});
var Url = subtype(Str, function (s) {
return s.indexOf('http://') === 0;
});
var Category = enums({ audio: 0, video: 1 });
var Price = struct({ currency: Str, amount: Num });
// JSON of a product
var json = {
name: 'iPod',
desc: 'Engineered for maximum funness.',
home: 'http://www.apple.com/ipod/',
shippings: ['Same Day', 'Next Businness Day'],
category: 'audio',
price: {currency: 'EUR', amount: 100},
dim: [2.4, 4.1]
};
// get an immutable instance, `new` is optional
var ipod = Product(json);
You have existing code and you want to add safety
// your code: plain old JavaScript class
function Point (x, y) {
this.x = x;
this.y = y;
}
var p = new Point(1, 'a'); // silent error
in order to "tcombify" your code you can simply add some asserts
function Point (x, y) {
assert(Num.is(x));
assert(Num.is(y));
this.x = x;
this.y = y;
}
var p = new Point(1, 'a'); // => fail! debugger kicks in
Node
npm install tcomb
Browser
bower install tcomb
or download the build/tcomb.min.js
file.
This library uses a few ES5 methods
Array.forEach()
Array.map()
Array.some()
Array.every()
Object.keys()
Object.freeze()
JSON.stringify()
you can use es5-shim
, es5-sham
and json2
to support old browsers
<!--[if lt IE 9]>
<script src="json2.js"></script>
<script src="es5-shim.min.js"></script>
<script src="es5-sham.min.js"></script>
<![endif]-->
<script type="text/javascript" src="tcomb.js"></script>
<script type="text/javascript">
console.log(t);
</script>
Run mocha
or npm test
in the project root.
What's a type? In tcomb a type is a function T
such that
T
has signatureT(value, [mut])
wherevalue
depends on the nature ofT
and the optional booleanmut
makes the instance mutable (defaultfalse
)T
is idempotent:T(T(value, mut), mut) === T(value, mut)
T
owns a static functionT.is(x)
returningtrue
ifx
is a instance ofT
Note: 2. implies that T
can be used as a default JSON decoder
In production envs you don't want to leak failures to the user
// override onFail hook
options.onFail = function (message) {
try {
// capture stack trace
throw new Error(message);
} catch (e) {
// use you favourite JavaScript error logging service
console.log(e.stack);
}
};
TODO: better docs
Adds to structs, tuples and lists a static method update
that returns a new instance
without modifying the original.
Example
// see http://facebook.github.io/react/docs/update.html
options.update = React.addons.update;
var p1 = Point({x: 0, y: 0});
var p2 = Point.update(p1, {x: {$set: 1}}); // => Point({x: 1, y: 0})
If guard !== true
the debugger kicks in.
guard
boolean conditionmessage
optional string useful for debugging, formatted with values like [util.format in Node][http://nodejs.org/api/util.html#util_util_format_format]
Example
assert(1 === 2); // throws 'assert(): failed'
assert(1 === 2, 'error!'); // throws 'error!'
assert(1 === 2, 'error: %s !== %s', 1, 2); // throws 'error: 1 !== 2'
To customize failure behaviuor, see options.onFail
.
Because sometimes you really gonna need it.
Any.is(..whatever..); // => true
Defines a struct like type.
props
hash name -> typename
optional string useful for debugging
Example
"use strict";
// defines a struct with two numerical props
var Point = struct({
x: Num,
y: Num
});
// methods are defined as usual
Point.prototype.toString = function () {
return '(' + this.x + ', ' + this.y + ')';
};
// costructor usage, p is immutable
var p = Point({x: 1, y: 2});
p.x = 2; // => TypeError
p = Point({x: 1, y: 2}, true); // now p is mutable
p.x = 2; // ok
Returns true
if x
is an instance of the struct.
Point.is(p); // => true
Defines a union of types.
types
array of typesname
optional string useful for debugging
Example
var Circle = struct({
center: Point,
radius: Num
});
var Rectangle = struct({
bl: Point, // bottom left vertex
tr: Point // top right vertex
});
var Shape = union([
Circle,
Rectangle
]);
Returns true
if x
belongs to the union.
Shape.is(Circle({center: p, radius: 10})); // => true
Same as union([Nil, type])
.
// the value of a radio input where null = no selection
var Radio = maybe(Str);
Radio.is('a'); // => true
Radio.is(null); // => true
Radio.is(1); // => false
Defines an enum of strings.
map
hash enum -> valuename
optional string useful for debugging
Example
var Direction = enums({
North: 0,
East: 1,
South: 2,
West: 3
});
Returns true
if x
belongs to the enum.
Direction.is('North'); // => true
Returns an enums of an array of keys, useful when you don't mind to define custom values for the enums.
keys
array (or string) of keysname
optional string useful for debugging
Example
// result is the same as the main example
var Direction = enums.of(['North', 'East', 'South', 'West']);
// or..
Direction = enums.of('North East South West');
Defines a tuple whose coordinates have the specified types.
types
array of coordinates typesname
optional string useful for debugging
Example
var Area = tuple([Num, Num]);
// constructor usage, area is immutable
var area = Area([1, 2]);
Returns true
if x
belongs to the tuple.
Area.is([1, 2]); // => true
Area.is([1, 'a']); // => false, the second element is not a Num
Area.is([1, 2, 3]); // => false, too many elements
Defines a subtype of an existing type.
type
the supertypepredicate
a function with signature(x) -> boolean
name
optional string useful for debugging
Example
// points of the first quadrant
var Q1Point = subtype(Point, function (p) {
return p.x >= 0 && p.y >= 0;
});
// costructor usage, p is immutable
var p = Q1Point({x: 1, y: 2});
p = Q1Point({x: -1, y: -2}); // => fail!
Returns true
if x
belongs to the subtype.
var Int = subtype(Num, function (n) {
return n === parseInt(n, 10);
});
Int.is(2); // => true
Int.is(1.1); // => false
Defines an array where all the elements are of type type
.
type
type of all the elementsname
optional string useful for debugging
Example
var Path = list(Point);
// costructor usage, path is immutable
var path = Path([
{x: 0, y: 0},
{x: 1, y: 1}
]);
Returns true
if x
belongs to the list.
var p1 = Point({x: 0, y: 0});
var p2 = Point({x: 1, y: 2});
Path.is([p1, p2]); // => true
Experimental. Defines a function where the arguments
and the return value are checked.
Arguments
the type ofarguments
f
the function to executeReturn
optional, check the type of the return valuename
optional string useful for debugging
Example
var sum = func(tuple([Num, Num]), function (a, b) {
return a + b;
}, Num);
sum(1, 2); // => 3
sum(1, 'a'); // => fail!
- explore generating UI based on domain models written with tcomb
- explore auto validation of UI involving domain models written with tcomb
- explore using tcomb with React.js
If you do have a contribution for the package feel free to put up a Pull Request or open an Issue.
The MIT License (MIT)
Copyright (c) 2014 Giulio Canti
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.