Comparing version 2.2.0 to 2.2.1
@@ -13,3 +13,9 @@ # Changelog | ||
**Note**: Gaps between patch versions are faulty/broken releases. | ||
**Note**: A feature tagged as Experimental is in a high state of flux, you're at risk of it changing without notice. | ||
## v2.2.1 | ||
- **Experimental** | ||
- pattern matching #121 | ||
## v2.2.0 | ||
@@ -16,0 +22,0 @@ |
158
GUIDE.md
@@ -10,3 +10,3 @@ # Setup | ||
```js | ||
import t from 'tcomb'; | ||
var t = require('tcomb'); | ||
``` | ||
@@ -50,3 +50,3 @@ | ||
```js | ||
const t = require('tcomb'); | ||
var t = require('tcomb'); | ||
@@ -75,4 +75,4 @@ t.String.is('a string'); // => true | ||
```js | ||
const x = -2; | ||
const min = 0; | ||
var x = -2; | ||
var min = 0; | ||
// throws "-2 should be greater then 0" | ||
@@ -85,4 +85,4 @@ assert(x > min, `${x} should be greater then ${min}`); | ||
```js | ||
const s1 = t.String('a string'); // => ok | ||
const s2 = t.String(1); // => throws | ||
var s1 = t.String('a string'); // => ok | ||
var s2 = t.String(1); // => throws | ||
``` | ||
@@ -101,3 +101,3 @@ | ||
const p = new Point(1, 'a'); // silent error | ||
var p = new Point(1, 'a'); // silent error | ||
@@ -111,3 +111,3 @@ // Now with asserts inserted: | ||
const p = new Point(1, 'a'); // => throws | ||
var p = new Point(1, 'a'); // => throws | ||
``` | ||
@@ -120,6 +120,6 @@ | ||
```js | ||
const t = require('tcomb'); | ||
const React = require('react'); | ||
var t = require('tcomb'); | ||
var React = require('react'); | ||
const ReactElement = t.irreducible('ReactElement', React.isValidElement); | ||
var ReactElement = t.irreducible('ReactElement', React.isValidElement); | ||
@@ -167,3 +167,3 @@ ReactElement.is(<div/>); // => true | ||
// defines a type representing positive numbers | ||
const Positive = t.subtype(t.Number, (n) => n >= 0, 'Positive'); | ||
var Positive = t.subtype(t.Number, (n) => n >= 0, 'Positive'); | ||
@@ -192,3 +192,3 @@ Positive.is(1); // => true | ||
```js | ||
const Country = t.enums({ | ||
var Country = t.enums({ | ||
IT: 'Italy', | ||
@@ -218,9 +218,9 @@ US: 'United States' | ||
// values will mirror the keys | ||
const Country = t.enums.of('IT US', 'Country'); | ||
var Country = t.enums.of('IT US', 'Country'); | ||
// same as | ||
const Country = t.enums.of(['IT', 'US'], 'Country'); | ||
var Country = t.enums.of(['IT', 'US'], 'Country'); | ||
// same as | ||
const Country = t.enums({ | ||
var Country = t.enums({ | ||
IT: 'IT', | ||
@@ -239,3 +239,3 @@ US: 'US' | ||
```js | ||
const Point = t.struct({ | ||
var Point = t.struct({ | ||
x: t.Number, | ||
@@ -246,3 +246,3 @@ y: t.Number | ||
// constructor usage, `p` is immutable, new is optional | ||
const p = new Point({x: 1, y: 2}); | ||
var p = new Point({x: 1, y: 2}); | ||
@@ -281,10 +281,10 @@ Point.is(p); // => true | ||
```js | ||
const Point3D = Point.extend({z: t.Number}, 'Point3D'); | ||
var Point3D = Point.extend({z: t.Number}, 'Point3D'); | ||
// multiple inheritance | ||
const A = struct({...}); | ||
const B = struct({...}); | ||
const MixinC = {...}; | ||
const MixinD = {...}; | ||
const E = A.extend([B, MixinC, MixinD]); | ||
var A = struct({...}); | ||
var B = struct({...}); | ||
var MixinC = {...}; | ||
var MixinD = {...}; | ||
var E = A.extend([B, MixinC, MixinD]); | ||
``` | ||
@@ -295,3 +295,3 @@ | ||
```js | ||
const Rectangle = t.struct({ | ||
var Rectangle = t.struct({ | ||
width: t.Number, | ||
@@ -305,3 +305,3 @@ height: t.Number | ||
const Cube = Rectangle.extend({ | ||
var Cube = Rectangle.extend({ | ||
thickness: t.Number | ||
@@ -319,3 +319,3 @@ }); | ||
```js | ||
const Wrong = Point.extend({x: t.Number}); // => throws | ||
var Wrong = Point.extend({x: t.Number}); // => throws | ||
``` | ||
@@ -333,6 +333,6 @@ | ||
```js | ||
const Area = t.tuple([t.Number, t.Number]); | ||
var Area = t.tuple([t.Number, t.Number]); | ||
// constructor usage, `area` is immutable | ||
const area = Area([1, 2]); | ||
var area = Area([1, 2]); | ||
``` | ||
@@ -359,6 +359,6 @@ | ||
```js | ||
const Path = t.list(Point); | ||
var Path = t.list(Point); | ||
// costructor usage, `path` is immutable | ||
const path = Path([ | ||
var path = Path([ | ||
{x: 0, y: 0}, // tcomb hydrates automatically using the `Point` constructor | ||
@@ -389,6 +389,6 @@ {x: 1, y: 1} | ||
```js | ||
const Tel = dict(String, t.Number); | ||
var Tel = dict(String, t.Number); | ||
// costructor usage, `tel` is immutable | ||
const tel = Tel({'jack': 4098, 'sape': 4139}); | ||
var tel = Tel({'jack': 4098, 'sape': 4139}); | ||
``` | ||
@@ -414,3 +414,3 @@ | ||
```js | ||
const ReactKey = t.union([t.String, t.Number]); | ||
var ReactKey = t.union([t.String, t.Number]); | ||
@@ -448,3 +448,3 @@ ReactKey.is('a'); // => true | ||
// now you can do this without a fail | ||
const key = ReactKey('a'); | ||
var key = ReactKey('a'); | ||
``` | ||
@@ -462,5 +462,5 @@ | ||
```js | ||
const Min = t.subtype(t.String, function (s) { return s.length > 2; }, 'Min'); | ||
const Max = t.subtype(t.String, function (s) { return s.length < 5; }, 'Max'); | ||
const MinMax = t.intersection([Min, Max], 'MinMax'); | ||
var Min = t.subtype(t.String, function (s) { return s.length > 2; }, 'Min'); | ||
var Max = t.subtype(t.String, function (s) { return s.length < 5; }, 'Max'); | ||
var MinMax = t.intersection([Min, Max], 'MinMax'); | ||
@@ -492,3 +492,3 @@ MinMax.is('abc'); // => true | ||
// the value of a radio input where null = no selection | ||
const Radio = t.maybe(t.String); | ||
var Radio = t.maybe(t.String); | ||
@@ -515,3 +515,3 @@ Radio.is('a'); // => true | ||
// add takes two `t.Number`s and returns a `t.Number` | ||
const add = t.func([t.Number, t.Number], t.Number) | ||
var add = t.func([t.Number, t.Number], t.Number) | ||
.of(function (x, y) { return x + y; }); | ||
@@ -542,3 +542,3 @@ ``` | ||
// An `A` takes a `t.String` and returns an `t.Number` | ||
const A = t.func(t.String, t.Number); | ||
var A = t.func(t.String, t.Number); | ||
``` | ||
@@ -550,9 +550,9 @@ | ||
// A `B` takes a `Func` (which takes a `t.String` and returns a `t.Number`) and returns a `t.String`. | ||
const B = t.func(t.func(t.String, t.Number), t.String); | ||
var B = t.func(t.func(t.String, t.Number), t.String); | ||
// An `ExcitedString` is a `t.String` containing an exclamation mark | ||
const ExcitedString = t.subtype(t.String, function (s) { return s.indexOf('!') !== -1; }, 'ExcitedString'); | ||
var ExcitedString = t.subtype(t.String, function (s) { return s.indexOf('!') !== -1; }, 'ExcitedString'); | ||
// An `Exciter` takes a `t.String` and returns an `ExcitedString` | ||
const Exciter = t.func(t.String, ExcitedString); | ||
var Exciter = t.func(t.String, ExcitedString); | ||
``` | ||
@@ -564,3 +564,3 @@ | ||
// A `C` takes an `A`, a `B` and a `t.String` and returns a `t.Number` | ||
const C = t.func([A, B, t.String], t.Number); | ||
var C = t.func([A, B, t.String], t.Number); | ||
``` | ||
@@ -589,4 +589,4 @@ | ||
```js | ||
const simpleQuestionator = Exciter.of(function (s) { return s + '?'; }); | ||
const simpleExciter = Exciter.of(function (s) { return s + '!'; }); | ||
var simpleQuestionator = Exciter.of(function (s) { return s + '?'; }); | ||
var simpleExciter = Exciter.of(function (s) { return s + '!'; }); | ||
@@ -609,8 +609,8 @@ // Raises error: | ||
// add : t.Number -> t.Number -> t.Number | ||
const add = t.func([t.Number, t.Number], t.Number) | ||
var add = t.func([t.Number, t.Number], t.Number) | ||
.of(function (x, y) { return x + y }, true); | ||
const addHello = add("Hello"); // As this raises: "Error: Invalid `Hello` supplied to `t.Number`" | ||
var addHello = add("Hello"); // As this raises: "Error: Invalid `Hello` supplied to `t.Number`" | ||
const add2 = add(2); | ||
var add2 = add(2); | ||
add2(1); // And this returns: 3 | ||
@@ -631,3 +631,3 @@ ``` | ||
const id = function (x) { return x; }; | ||
var id = function (x) { return x; }; | ||
@@ -664,3 +664,3 @@ t.func([t.Number, t.Number], t.Number).is(func([t.Number, t.Number], t.Number).of(id)); // Returns: true | ||
```js | ||
const p = new Point({x: 1, y: 2}); | ||
var p = new Point({x: 1, y: 2}); | ||
@@ -673,5 +673,5 @@ p = Point.update(p, {x: {'$set': 3}}); // => {x: 3, y: 2} | ||
```js | ||
const MyType = dict(t.String, t.Number); | ||
const instance = MyType({a: 1, b: 2}); | ||
const updated = MyType.update(instance, {$remove: ['a']}); // => {b: 2} | ||
var MyType = dict(t.String, t.Number); | ||
var instance = MyType({a: 1, b: 2}); | ||
var updated = MyType.update(instance, {$remove: ['a']}); // => {b: 2} | ||
``` | ||
@@ -682,5 +682,5 @@ | ||
```js | ||
const MyType = list(t.Number); | ||
const instance = MyType([1, 2, 3, 4]); | ||
const updated = MyType.update(instance, {'$swap': {from: 1, to: 2}}); // => [1, 3, 2, 4] | ||
var MyType = list(t.Number); | ||
var instance = MyType([1, 2, 3, 4]); | ||
var updated = MyType.update(instance, {'$swap': {from: 1, to: 2}}); // => [1, 3, 2, 4] | ||
``` | ||
@@ -692,2 +692,38 @@ | ||
# Pattern matching | ||
```js | ||
match(x: t.Any, cases...) | ||
``` | ||
where each case has the following structure | ||
``` | ||
type, [guard], result | ||
``` | ||
- `type` a tcomb type | ||
- `guard` an optional predicate `(x) => t.Any` | ||
- `result` a function `(x) => t.Any` called when the match succeded | ||
Example: | ||
```js | ||
// this example uses ES6 syntax | ||
const A = t.struct({...}); | ||
const result = t.match(1, | ||
t.String, (s) => 'a string', | ||
t.Number, (n) => n > 2, (n) => 'a number gt 2', // case with a guard (optional) | ||
t.Number, (n) => 'a number lte 2', | ||
A, (a) => 'an instance of A', | ||
t.Any, (x) => 'other...' // catch all | ||
); | ||
console.log(result); // => 'a number lte 2' | ||
``` | ||
**Note**. If a match is not found it will fail with a `Match error`. | ||
# Utils | ||
@@ -699,2 +735,3 @@ | ||
Called when an assert fails. | ||
The default behaviour when failures occur is to throw a TypeError: | ||
@@ -718,2 +755,3 @@ | ||
Immutability helper. | ||
You can override the default behaviour re-defining the `t.update` function: | ||
@@ -729,3 +767,3 @@ | ||
Returns a type's name: | ||
Returns the name of a tcomb type: | ||
@@ -736,3 +774,3 @@ ```js | ||
If name is not specified, fallbacks according to [http://flowtype.org](http://flowtype.org) | ||
If a name is not specified when defining the type, a default name will be provided according to [http://flowtype.org](http://flowtype.org). | ||
@@ -739,0 +777,0 @@ ## mixin(target: Object, source: Object, override?: boolean): Object |
205
index.js
'use strict'; | ||
function stringify(x) { | ||
// configurable | ||
exports.stringify = function stringify(x) { | ||
try { // handle "Converting circular structure to JSON" error | ||
@@ -9,6 +10,4 @@ return JSON.stringify(x, null, 2); | ||
} | ||
} | ||
}; | ||
exports.stringify = stringify; | ||
function isInstanceOf(x, constructor) { | ||
@@ -64,5 +63,3 @@ return x instanceof constructor; | ||
function is(x, type) { | ||
return isType(type) ? | ||
type.is(x) : | ||
isInstanceOf(x, type); // type should be a class constructor | ||
return isType(type) ? type.is(x) : isInstanceOf(x, type); // type should be a class constructor | ||
} | ||
@@ -72,6 +69,4 @@ | ||
if (isType(type)) { | ||
return isStruct(type) ? | ||
// for structs the new operator is allowed | ||
new type(value) : | ||
type(value); | ||
// for structs the new operator is allowed | ||
return isStruct(type) ? new type(value) : type(value); | ||
} | ||
@@ -92,5 +87,3 @@ | ||
function getTypeName(constructor) { | ||
if (isType(constructor)) { | ||
return constructor.displayName; | ||
} | ||
if (isType(constructor)) { return constructor.displayName; } | ||
return getFunctionName(constructor); | ||
@@ -130,8 +123,6 @@ } | ||
function shallowCopy(x) { | ||
return isArray(x) ? | ||
x.concat() : | ||
isObject(x) ? | ||
mixin({}, x) : | ||
x; | ||
function getShallowCopy(x) { | ||
if (isArray(x)) { return x.concat(); } | ||
if (isObject(x)) { return mixin({}, x); } | ||
return x; | ||
} | ||
@@ -146,3 +137,3 @@ | ||
var value = shallowCopy(instance); | ||
var value = getShallowCopy(instance); | ||
for (var k in spec) { | ||
@@ -161,82 +152,79 @@ if (spec.hasOwnProperty(k)) { | ||
update.commands = { | ||
// built-in commands | ||
$apply: function (f, value) { | ||
function $apply(f, value) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isFunction(f), 'Invalid argument f supplied to immutability helper { $apply: f }: expected a function'); | ||
} | ||
return f(value); | ||
} | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isFunction(f), 'Invalid argument f supplied to immutability helper { $apply: f }: expected a function'); | ||
} | ||
function $push(elements, arr) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isArray(elements), 'Invalid argument elements supplied to immutability helper { $push: elements }: expected an array'); | ||
assert(isArray(arr), 'Invalid value supplied to immutability helper "$push": expected an array'); | ||
} | ||
return arr.concat(elements); | ||
} | ||
return f(value); | ||
}, | ||
function $remove(keys, obj) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isArray(keys), 'Invalid argument keys supplied to immutability helper { $remove: keys }: expected an array'); | ||
assert(isObject(obj), 'Invalid value supplied to immutability helper $remove: expected an object'); | ||
} | ||
for (var i = 0, len = keys.length; i < len; i++ ) { | ||
delete obj[keys[i]]; | ||
} | ||
return obj; | ||
} | ||
$push: function (elements, arr) { | ||
function $set(value) { | ||
return value; | ||
} | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isArray(elements), 'Invalid argument elements supplied to immutability helper { $push: elements }: expected an array'); | ||
assert(isArray(arr), 'Invalid value supplied to immutability helper "$push": expected an array'); | ||
} | ||
function $splice(splices, arr) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(list(Arr).is(splices), 'Invalid argument splices supplied to immutability helper { $splice: splices }: expected an array of arrays'); | ||
assert(isArray(arr), 'Invalid value supplied to immutability helper $splice: expected an array'); | ||
} | ||
return splices.reduce(function (acc, splice) { | ||
acc.splice.apply(acc, splice); | ||
return acc; | ||
}, arr); | ||
} | ||
return arr.concat(elements); | ||
}, | ||
function $swap(config, arr) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isObject(config), 'Invalid argument config supplied to immutability helper { $swap: config }: expected an object'); | ||
assert(isNumber(config.from), 'Invalid argument config.from supplied to immutability helper { $swap: config }: expected a number'); | ||
assert(isNumber(config.to), 'Invalid argument config.to supplied to immutability helper { $swap: config }: expected a number'); | ||
assert(isArray(arr), 'Invalid value supplied to immutability helper $swap'); | ||
} | ||
var element = arr[config.to]; | ||
arr[config.to] = arr[config.from]; | ||
arr[config.from] = element; | ||
return arr; | ||
} | ||
$remove: function (keys, obj) { | ||
function $unshift(elements, arr) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isArray(elements), 'Invalid argument elements supplied to immutability helper {$unshift: elements}'); | ||
assert(isArray(arr), 'Invalid value supplied to immutability helper $unshift'); | ||
} | ||
return elements.concat(arr); | ||
} | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isArray(keys), 'Invalid argument keys supplied to immutability helper { $remove: keys }: expected an array'); | ||
assert(isObject(obj), 'Invalid value supplied to immutability helper $remove: expected an object'); | ||
} | ||
function $merge(obj, value) { | ||
return mixin(mixin({}, value), obj, true); | ||
} | ||
for (var i = 0, len = keys.length; i < len; i++ ) { | ||
delete obj[keys[i]]; | ||
} | ||
return obj; | ||
}, | ||
$set: function (value) { | ||
return value; | ||
}, | ||
$splice: function (splices, arr) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(list(Arr).is(splices), 'Invalid argument splices supplied to immutability helper { $splice: splices }: expected an array of arrays'); | ||
assert(isArray(arr), 'Invalid value supplied to immutability helper $splice: expected an array'); | ||
} | ||
return splices.reduce(function (acc, splice) { | ||
acc.splice.apply(acc, splice); | ||
return acc; | ||
}, arr); | ||
}, | ||
$swap: function (config, arr) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isObject(config), 'Invalid argument config supplied to immutability helper { $swap: config }: expected an object'); | ||
assert(isNumber(config.from), 'Invalid argument config.from supplied to immutability helper { $swap: config }: expected a number'); | ||
assert(isNumber(config.to), 'Invalid argument config.to supplied to immutability helper { $swap: config }: expected a number'); | ||
assert(isArray(arr), 'Invalid value supplied to immutability helper $swap'); | ||
} | ||
var element = arr[config.to]; | ||
arr[config.to] = arr[config.from]; | ||
arr[config.from] = element; | ||
return arr; | ||
}, | ||
$unshift: function (elements, arr) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isArray(elements), 'Invalid argument elements supplied to immutability helper {$unshift: elements}'); | ||
assert(isArray(arr), 'Invalid value supplied to immutability helper $unshift'); | ||
} | ||
return elements.concat(arr); | ||
}, | ||
$merge: function (obj, value) { | ||
return mixin(mixin({}, value), obj, true); | ||
} | ||
update.commands = { | ||
$apply: $apply, | ||
$push: $push, | ||
$remove: $remove, | ||
$set: $set, | ||
$splice: $splice, | ||
$swap: $swap, | ||
$unshift: $unshift, | ||
$merge: $merge | ||
}; | ||
@@ -920,2 +908,32 @@ | ||
function match(x) { | ||
var type, guard, f, count; | ||
for (var i = 1, len = arguments.length; i < len; ) { | ||
type = arguments[i]; | ||
guard = arguments[i + 1]; | ||
f = arguments[i + 2]; | ||
if (isFunction(f) && !isType(f)) { | ||
i = i + 3; | ||
} | ||
else { | ||
f = guard; | ||
guard = Any.is; | ||
i = i + 2; | ||
} | ||
if (process.env.NODE_ENV !== 'production') { | ||
count = (count || 0) + 1; | ||
assert(isType(type), 'Invalid type in clause #' + count); | ||
assert(isFunction(guard), 'Invalid guard in clause #' + count + ''); | ||
assert(isFunction(f), 'Invalid block in clause #' + count + ''); | ||
} | ||
if (type.is(x) && guard(x)) { | ||
return f(x); | ||
} | ||
} | ||
exports.fail('Match error'); | ||
} | ||
mixin(exports, { | ||
@@ -958,3 +976,4 @@ is: is, | ||
func: func, | ||
intersection: intersection | ||
intersection: intersection, | ||
match: match | ||
}); |
{ | ||
"name": "tcomb", | ||
"version": "2.2.0", | ||
"version": "2.2.1", | ||
"description": "Type checking and DDD for JavaScript", | ||
@@ -21,3 +21,3 @@ "main": "index.js", | ||
"devDependencies": { | ||
"eslint": "^0.22.1", | ||
"eslint": "^1.2.0", | ||
"mocha": "^2.2.5" | ||
@@ -24,0 +24,0 @@ }, |
@@ -13,9 +13,9 @@ [![build status](https://img.shields.io/travis/gcanti/tcomb/master.svg?style=flat-square)](https://travis-ci.org/gcanti/tcomb) | ||
```js | ||
import t from 'tcomb'; | ||
var t = require('tcomb'); | ||
// a user defined type | ||
const Integer = t.subtype(t.Number, (n) => n % 1 === 0); | ||
var Integer = t.subtype(t.Number, function (n) => { return n % 1 === 0; }); | ||
// a struct | ||
const Person = t.struct({ | ||
var Person = t.struct({ | ||
name: t.String, // required string | ||
@@ -33,3 +33,3 @@ surname: t.maybe(t.String), // optional string | ||
// an instance of Person (the keyword new is optional) | ||
const person = new Person({ | ||
var person = new Person({ | ||
name: 'Giulio', | ||
@@ -76,3 +76,3 @@ surname: 'Canti', | ||
```js | ||
const person = new Person({ | ||
var person = new Person({ | ||
name: 'Giulio', | ||
@@ -97,3 +97,3 @@ // missing required field "age" | ||
```js | ||
const person2 = Person.update(person, { | ||
var person2 = Person.update(person, { | ||
name: {$set: 'Guido'} | ||
@@ -150,2 +150,15 @@ }); | ||
## Pattern matching | ||
```js | ||
// this example uses ES6 syntax | ||
const result = t.match(1, | ||
t.String, () => 'a string', | ||
t.Number, () => 'a number' | ||
); | ||
console.log(result); // => 'a number' | ||
``` | ||
# Docs | ||
@@ -160,4 +173,15 @@ | ||
# Similar projects | ||
* [immu](https://github.com/scottcorgan/immu) | ||
* [immutable](https://github.com/facebook/immutable-js) | ||
* [mori](https://github.com/swannodette/mori) | ||
* [seamless-immutable](https://github.com/rtfeldman/seamless-immutable) | ||
* [deep-freeze](https://www.npmjs.com/package/deep-freeze) | ||
* [freezer](https://github.com/arqex/freezer) | ||
* [icedam](https://github.com/winkler1/icedam) | ||
* [immutable-store](https://github.com/christianalfoni/immutable-store) | ||
# License | ||
The MIT License (MIT) |
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
52427
756
182