Comparing version 3.0.0 to 3.1.0
@@ -15,2 +15,19 @@ # Changelog | ||
# v3.1.0 | ||
- **New Feature** | ||
- add `t.Integer` to standard types | ||
- add `t.Type` to standard types | ||
- `interface` combinator, fix #195, [docs](https://github.com/gcanti/tcomb/blob/master/docs/API.md#the-interface-combinator) (thanks @ctrlplusb) | ||
- add interface support to fromJSON (@minedeljkovic) | ||
- add support for extending refinements, fix #179, [docs](https://github.com/gcanti/tcomb/blob/master/docs/API.md#extending-structs) | ||
- local and global `strict` option for structs and interfaces, fix #203, [docs](https://github.com/gcanti/tcomb/blob/master/docs/API.md#strictness) | ||
- Chrome Dev Tools custom formatter for tcomb types [docs](https://github.com/gcanti/tcomb/blob/master/docs/API.md#the-libinstalltypeformatter-module) | ||
- **Bug Fix** | ||
- More intelligent immutability update handling, fix #199 (thanks @ctrlplusb) | ||
- func combinator: support optional arguments, fix #198 (thanks @ivan-kleshnin) | ||
- **Internal** | ||
- add "Struct" prefix to structs default name | ||
- `mixin()` now allows identical references for overlapping properties | ||
# v3.0.0 | ||
@@ -45,4 +62,4 @@ | ||
- **Documentation** | ||
- revamp [docs/API.md](docs/API.md) | ||
- add ["A little guide to runtime type checking and runtime type introspection"](docs/GUIDE.md) (WIP) | ||
- revamp [API.md](https://github.com/gcanti/tcomb/blob/master/docs/API.md) | ||
- add ["A little guide to runtime type checking and runtime type introspection"](https://github.com/gcanti/tcomb/blob/master/docs/GUIDE.md) (WIP) | ||
@@ -49,0 +66,0 @@ ## v2.5.2 |
@@ -85,3 +85,3 @@ declare module Tcomb { | ||
type StructProps = {[key: string]: Constructor<any>}; | ||
type StructMixin = StructProps | Struct<any>; | ||
type StructMixin = StructProps | Struct<any> | Interface<any>; | ||
@@ -103,2 +103,19 @@ interface Struct<T> extends Type<T> { | ||
// | ||
// interface | ||
// | ||
interface Interface<T> extends Type<T> { | ||
meta: { | ||
kind: string; | ||
name: string; | ||
identity: boolean; | ||
props: StructProps; | ||
}; | ||
update: Update<T>; | ||
extend<E extends T>(mixins: StructMixin | Array<StructMixin>, name?: string): Struct<E>; | ||
} | ||
export function interface<T>(props: StructProps, name?: string): Interface<T>; | ||
// | ||
// list | ||
@@ -105,0 +122,0 @@ // |
/*! @preserve | ||
* | ||
* tcomb.js - Type checking and DDD for JavaScript | ||
* | ||
* The MIT License (MIT) | ||
@@ -21,5 +23,7 @@ * | ||
t.Number = require('./lib/Number'); | ||
t.Integer = require('./lib/Integer'); | ||
t.Object = require('./lib/Object'); | ||
t.RegExp = require('./lib/RegExp'); | ||
t.String = require('./lib/String'); | ||
t.Type = require('./lib/Type'); | ||
@@ -51,2 +55,4 @@ // short alias are deprecated | ||
t.subtype = t.refinement; | ||
t.inter = require('./lib/interface'); // IE8 alias | ||
t['interface'] = t.inter; | ||
@@ -53,0 +59,0 @@ // functions |
@@ -52,2 +52,17 @@ var assert = require('./assert'); | ||
case 'interface' : | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isObject(value), function () { | ||
return 'Invalid argument value ' + assert.stringify(value) + ' supplied to fromJSON(value, type) (expected an object)'; | ||
}); | ||
} | ||
var interProps = type.meta.props; | ||
ret = {}; | ||
for (k in interProps) { | ||
if (interProps.hasOwnProperty(k)) { | ||
ret[k] = fromJSON(value[k], interProps[k]); | ||
} | ||
} | ||
return ret; | ||
case 'list' : | ||
@@ -54,0 +69,0 @@ if (process.env.NODE_ENV !== 'production') { |
@@ -13,2 +13,3 @@ var assert = require('./assert'); | ||
var getTypeName = require('./getTypeName'); | ||
var isType = require('./isType'); | ||
@@ -23,2 +24,16 @@ function getDefaultName(domain, codomain) { | ||
function getOptionalArgumentsIndex(types) { | ||
var end = types.length; | ||
var areAllMaybes = false; | ||
for (var i = end - 1; i >= 0; i--) { | ||
var type = types[i]; | ||
if (!isType(type) || type.meta.kind !== 'maybe') { | ||
return (i + 1); | ||
} else { | ||
areAllMaybes = true; | ||
} | ||
} | ||
return areAllMaybes ? 0 : end; | ||
} | ||
function func(domain, codomain, name) { | ||
@@ -35,2 +50,4 @@ | ||
var displayName = name || getDefaultName(domain, codomain); | ||
var domainLength = domain.length; | ||
var optionalArgumentsIndex = getOptionalArgumentsIndex(domain); | ||
@@ -62,3 +79,3 @@ function FuncType(value, curried) { | ||
return isInstrumented(x) && | ||
x.instrumentation.domain.length === domain.length && | ||
x.instrumentation.domain.length === domainLength && | ||
x.instrumentation.domain.every(function (type, i) { | ||
@@ -83,16 +100,20 @@ return type === domain[i]; | ||
var args = Array.prototype.slice.call(arguments); | ||
var len = curried ? | ||
args.length : | ||
domain.length; | ||
var argsType = tuple(domain.slice(0, len)); | ||
var argsLength = args.length; | ||
args = argsType(args); // type check arguments | ||
if (process.env.NODE_ENV !== 'production') { | ||
// type-check arguments | ||
var tupleLength = curried ? argsLength : Math.max(argsLength, optionalArgumentsIndex); | ||
tuple(domain.slice(0, tupleLength), 'arguments of function ' + displayName)(args); | ||
} | ||
if (len === domain.length) { | ||
return create(codomain, f.apply(this, args)); | ||
if (curried && argsLength < domainLength) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(argsLength > 0, 'Invalid arguments.length = 0 for curried function ' + displayName); | ||
} | ||
var g = Function.prototype.bind.apply(f, [this].concat(args)); | ||
var newDomain = func(domain.slice(argsLength), codomain); | ||
return newDomain.of(g, true); | ||
} | ||
else { | ||
var g = Function.prototype.bind.apply(f, [this].concat(args)); | ||
var newdomain = func(domain.slice(len), codomain); | ||
return newdomain.of(g, curried); | ||
return create(codomain, f.apply(this, args)); | ||
} | ||
@@ -118,2 +139,3 @@ } | ||
func.getDefaultName = getDefaultName; | ||
func.getOptionalArgumentsIndex = getOptionalArgumentsIndex; | ||
module.exports = func; |
@@ -11,3 +11,3 @@ var isNil = require('./isNil'); | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(!target.hasOwnProperty(k), function () { return 'Invalid call to mixin(target, source, [overwrite]): cannot overwrite property "' + k + '" of target object'; }); | ||
assert(!target.hasOwnProperty(k) || target[k] === source[k], function () { return 'Invalid call to mixin(target, source, [overwrite]): cannot overwrite property "' + k + '" of target object'; }); | ||
} | ||
@@ -14,0 +14,0 @@ } |
@@ -5,43 +5,39 @@ var assert = require('./assert'); | ||
var Function = require('./Function'); | ||
var isArray = require('./isArray'); | ||
var isBoolean = require('./isBoolean'); | ||
var isObject = require('./isObject'); | ||
var isNil = require('./isNil'); | ||
var create = require('./create'); | ||
var mixin = require('./mixin'); | ||
var isStruct = require('./isStruct'); | ||
var getTypeName = require('./getTypeName'); | ||
var dict = require('./dict'); | ||
var getDefaultInterfaceName = require('./getDefaultInterfaceName'); | ||
var extend = require('./extend'); | ||
function getDefaultName(props) { | ||
return '{' + Object.keys(props).map(function (prop) { | ||
return prop + ': ' + getTypeName(props[prop]); | ||
}).join(', ') + '}'; | ||
return 'Struct' + getDefaultInterfaceName(props); | ||
} | ||
function extend(mixins, name) { | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(isArray(mixins) && mixins.every(function (x) { | ||
return isObject(x) || isStruct(x); | ||
}), function () { return 'Invalid argument mixins supplied to extend(mixins, name), expected an array of objects or structs'; }); | ||
function extendStruct(mixins, name) { | ||
return extend(struct, mixins, name); | ||
} | ||
function getOptions(options) { | ||
if (!isObject(options)) { | ||
options = isNil(options) ? {} : { name: options }; | ||
} | ||
var props = {}; | ||
var prototype = {}; | ||
mixins.forEach(function (struct) { | ||
if (isObject(struct)) { | ||
mixin(props, struct); | ||
} | ||
else { | ||
mixin(props, struct.meta.props); | ||
mixin(prototype, struct.prototype); | ||
} | ||
}); | ||
var ret = struct(props, name); | ||
mixin(ret.prototype, prototype); | ||
return ret; | ||
if (!options.hasOwnProperty('strict')) { | ||
options.strict = struct.strict; | ||
} | ||
return options; | ||
} | ||
function struct(props, name) { | ||
function struct(props, options) { | ||
options = getOptions(options); | ||
var name = options.name; | ||
var strict = options.strict; | ||
if (process.env.NODE_ENV !== 'production') { | ||
assert(dict(String, Function).is(props), function () { return 'Invalid argument props ' + assert.stringify(props) + ' supplied to struct(props, [name]) combinator (expected a dictionary String -> Type)'; }); | ||
assert(isTypeName(name), function () { return 'Invalid argument name ' + assert.stringify(name) + ' supplied to struct(props, [name]) combinator (expected a string)'; }); | ||
assert(dict(String, Function).is(props), function () { return 'Invalid argument props ' + assert.stringify(props) + ' supplied to struct(props, [options]) combinator (expected a dictionary String -> Type)'; }); | ||
assert(isTypeName(name), function () { return 'Invalid argument name ' + assert.stringify(name) + ' supplied to struct(props, [options]) combinator (expected a string)'; }); | ||
assert(isBoolean(strict), function () { return 'Invalid argument strict ' + assert.stringify(strict) + ' supplied to struct(props, [options]) combinator (expected a boolean)'; }); | ||
} | ||
@@ -60,2 +56,10 @@ | ||
assert(isObject(value), function () { return 'Invalid value ' + assert.stringify(value) + ' supplied to ' + path.join('/') + ' (expected an object)'; }); | ||
// strictness | ||
if (strict) { | ||
for (k in value) { | ||
if (value.hasOwnProperty(k)) { | ||
assert(props.hasOwnProperty(k), function () { return 'Invalid additional prop "' + k + '" supplied to ' + path.join('/'); }); | ||
} | ||
} | ||
} | ||
} | ||
@@ -85,3 +89,4 @@ | ||
name: name, | ||
identity: false | ||
identity: false, | ||
strict: strict | ||
}; | ||
@@ -99,4 +104,4 @@ | ||
Struct.extend = function (structs, name) { | ||
return extend([Struct].concat(structs), name); | ||
Struct.extend = function (xs, name) { | ||
return extendStruct([Struct].concat(xs), name); | ||
}; | ||
@@ -107,4 +112,6 @@ | ||
struct.strict = false; | ||
struct.getOptions = getOptions; | ||
struct.getDefaultName = getDefaultName; | ||
struct.extend = extend; | ||
struct.extend = extendStruct; | ||
module.exports = struct; |
@@ -21,2 +21,10 @@ var assert = require('./assert'); | ||
function isCommand(k) { | ||
return update.commands.hasOwnProperty(k); | ||
} | ||
function getCommand(k) { | ||
return update.commands[k]; | ||
} | ||
function update(instance, patch) { | ||
@@ -28,12 +36,21 @@ | ||
var value = getShallowCopy(instance); | ||
var value = instance; | ||
var isChanged = false; | ||
var newValue; | ||
for (var k in patch) { | ||
if (patch.hasOwnProperty(k)) { | ||
if (update.commands.hasOwnProperty(k)) { | ||
value = update.commands[k](patch[k], value); | ||
isChanged = true; | ||
if (isCommand(k)) { | ||
newValue = getCommand(k)(patch[k], value); | ||
if (newValue !== instance) { | ||
isChanged = true; | ||
value = newValue; | ||
} else { | ||
value = instance; | ||
} | ||
} | ||
else { | ||
var newValue = update(value[k], patch[k]); | ||
if (value === instance) { | ||
value = getShallowCopy(instance); | ||
} | ||
newValue = update(value[k], patch[k]); | ||
isChanged = isChanged || ( newValue !== value[k] ); | ||
@@ -61,3 +78,6 @@ value[k] = newValue; | ||
} | ||
return arr.concat(elements); | ||
if (elements.length > 0) { | ||
return arr.concat(elements); | ||
} | ||
return arr; | ||
} | ||
@@ -70,4 +90,7 @@ | ||
} | ||
for (var i = 0, len = keys.length; i < len; i++ ) { | ||
delete obj[keys[i]]; | ||
if (keys.length > 0) { | ||
obj = getShallowCopy(obj); | ||
for (var i = 0, len = keys.length; i < len; i++ ) { | ||
delete obj[keys[i]]; | ||
} | ||
} | ||
@@ -86,6 +109,10 @@ return obj; | ||
} | ||
return splices.reduce(function (acc, splice) { | ||
acc.splice.apply(acc, splice); | ||
return acc; | ||
}, arr); | ||
if (splices.length > 0) { | ||
arr = getShallowCopy(arr); | ||
return splices.reduce(function (acc, splice) { | ||
acc.splice.apply(acc, splice); | ||
return acc; | ||
}, arr); | ||
} | ||
return arr; | ||
} | ||
@@ -100,5 +127,8 @@ | ||
} | ||
var element = arr[config.to]; | ||
arr[config.to] = arr[config.from]; | ||
arr[config.from] = element; | ||
if (config.from !== config.to) { | ||
arr = getShallowCopy(arr); | ||
var element = arr[config.to]; | ||
arr[config.to] = arr[config.from]; | ||
arr[config.from] = element; | ||
} | ||
return arr; | ||
@@ -112,7 +142,18 @@ } | ||
} | ||
return elements.concat(arr); | ||
if (elements.length > 0) { | ||
return elements.concat(arr); | ||
} | ||
return arr; | ||
} | ||
function $merge(obj, value) { | ||
return mixin(mixin({}, value), obj, true); | ||
function $merge(whatToMerge, value) { | ||
var isChanged = false; | ||
var result = getShallowCopy(value); | ||
for (var k in whatToMerge) { | ||
if (whatToMerge.hasOwnProperty(k)) { | ||
result[k] = whatToMerge[k]; | ||
isChanged = isChanged || ( result[k] !== value[k] ); | ||
} | ||
} | ||
return isChanged ? result : value; | ||
} | ||
@@ -119,0 +160,0 @@ |
{ | ||
"name": "tcomb", | ||
"version": "3.0.0", | ||
"version": "3.1.0", | ||
"description": "Type checking and DDD for JavaScript", | ||
@@ -14,3 +14,5 @@ "main": "index.js", | ||
"lint": "eslint index.js lib test", | ||
"test": "npm run lint && mocha" | ||
"test": "npm run lint && mocha", | ||
"dist": "webpack", | ||
"perf": "node ./perf/perf" | ||
}, | ||
@@ -28,4 +30,6 @@ "repository": { | ||
"devDependencies": { | ||
"benchmark": "2.1.0", | ||
"eslint": "1.10.3", | ||
"mocha": "2.3.4" | ||
"mocha": "2.3.4", | ||
"webpack": "1.12.14" | ||
}, | ||
@@ -32,0 +36,0 @@ "tags": [ |
@@ -96,2 +96,3 @@ [![build status](https://img.shields.io/travis/gcanti/tcomb/master.svg?style=flat-square)](https://travis-ci.org/gcanti/tcomb) | ||
* recursive and mutually recursive types | ||
* interfaces | ||
@@ -185,2 +186,29 @@ **Immutability and immutability helpers** | ||
# How to Build a standalone bundle | ||
```sh | ||
git clone git@github.com:gcanti/tcomb.git | ||
cd tcomb | ||
npm install | ||
npm run dist | ||
``` | ||
Will output 2 files: | ||
- `dist/tcomb.js` (development) | ||
- `dist/tcomb.min.js` (production) `Object.freeze` calls and asserts stripped out | ||
# Related libraries | ||
* [tcomb-doc](https://github.com/gcanti/tcomb-doc) Documentation tool for tcomb | ||
* [tcomb-validation](https://github.com/gcanti/tcomb-validation) Validation library based on type combinators | ||
* [tcomb-json-schema](https://github.com/gcanti/tcomb-json-schema) Transforms a JSON Schema to a tcomb type | ||
* [tcomb-defaults](https://github.com/ahdinosaur/tcomb-defaults) default properties in tcomb structs | ||
* [reactuate](https://github.com/reactuate/reactuate) React/Redux stack (not a boilerplate kit) | ||
* [tcomb-react](https://github.com/gcanti/tcomb-react) Alternative syntax for PropTypes | ||
* [mongorito-tcomb](https://github.com/xouabita/mongorito-tcomb) Bring schema validation to Mongorito thanks to tcomb | ||
* [tcomb-form](https://github.com/gcanti/tcomb-form) Forms library for react | ||
* [tcomb-form-types](https://github.com/Industrial/tcomb-form-types) Adds Types/Validations to tcomb-form | ||
* [tcomb-form-native](https://github.com/gcanti/tcomb-form-native) Forms library for react-native | ||
# Similar projects | ||
@@ -187,0 +215,0 @@ |
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
79227
62
1716
228
4
69