Repository: gcanti/tcomb
Pull request #225 (2016-07-09 08:52:08)

prevent bugs when enums are defined through `t.declare`


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 87ab76a..cbaf09b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,11 @@
 **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.
 
+# v3.2.5
+
+- **Polish**
+  - prevent bugs when enums are defined through `t.declare` (@gcanti)
+
 # v3.2.4
 
 - **Polish**
diff --git a/lib/create.js b/lib/create.js
index d1e79d1..fcf5814 100644
--- a/lib/create.js
+++ b/lib/create.js
@@ -6,7 +6,7 @@ var stringify = require('./stringify');
 // creates an instance of a type, handling the optional new operator
 module.exports = function create(type, value, path) {
   if (isType(type)) {
-    return type.meta.identity ? type(value, path): new type(value, path);
+    return !type.meta.identity && typeof value === 'object' ? new type(value, path): type(value, path);
   }
 
   if (process.env.NODE_ENV !== 'production') {
diff --git a/lib/declare.js b/lib/declare.js
index 043d52b..010d972 100644
--- a/lib/declare.js
+++ b/lib/declare.js
@@ -36,8 +36,7 @@ module.exports = function declare(name) {
       type.displayName = Declare.displayName = name;
       Declare.meta.name = name;
     }
-    // ensure identity is still false
-    Declare.meta.identity = false;
+    Declare.meta.identity = type.meta.identity;
     Declare.prototype = type.prototype;
     return Declare;
   };
diff --git a/lib/intersection.js b/lib/intersection.js
index 2e3e855..43b4613 100644
--- a/lib/intersection.js
+++ b/lib/intersection.js
@@ -5,6 +5,7 @@ var isArray = require('./isArray');
 var forbidNewOperator = require('./isIdentity');
 var is = require('./is');
 var getTypeName = require('./getTypeName');
+var isIdentity = require('./isIdentity');
 
 function getDefaultName(types) {
   return types.map(getTypeName).join(' & ');
@@ -18,11 +19,14 @@ function intersection(types, name) {
   }
 
   var displayName = name || getDefaultName(types);
+  var identity = types.every(isIdentity);
 
   function Intersection(value, path) {
 
     if (process.env.NODE_ENV !== 'production') {
-      forbidNewOperator(this, Intersection);
+      if (identity) {
+        forbidNewOperator(this, Intersection);
+      }
       path = path || [displayName];
       assert(Intersection.is(value), function () { return 'Invalid value ' + assert.stringify(value) + ' supplied to ' + path.join('/'); });
     }
@@ -34,7 +38,7 @@ function intersection(types, name) {
     kind: 'intersection',
     types: types,
     name: name,
-    identity: true
+    identity: identity
   };
 
   Intersection.displayName = displayName;
diff --git a/lib/maybe.js b/lib/maybe.js
index 3ecf921..01e1315 100644
--- a/lib/maybe.js
+++ b/lib/maybe.js
@@ -2,6 +2,7 @@ var assert = require('./assert');
 var isTypeName = require('./isTypeName');
 var isFunction = require('./isFunction');
 var isMaybe = require('./isMaybe');
+var isIdentity = require('./isIdentity');
 var Any = require('./Any');
 var create = require('./create');
 var Nil = require('./Nil');
@@ -25,10 +26,13 @@ function maybe(type, name) {
   }
 
   var displayName = name || getDefaultName(type);
+  var identity = isIdentity(type);
 
   function Maybe(value, path) {
     if (process.env.NODE_ENV !== 'production') {
-      forbidNewOperator(this, Maybe);
+      if (identity) {
+        forbidNewOperator(this, Maybe);
+      }
     }
     return Nil.is(value) ? value : create(type, value, path);
   }
@@ -37,7 +41,7 @@ function maybe(type, name) {
     kind: 'maybe',
     type: type,
     name: name,
-    identity: true
+    identity: identity
   };
 
   Maybe.displayName = displayName;
diff --git a/lib/refinement.js b/lib/refinement.js
index 231d446..3913ab4 100644
--- a/lib/refinement.js
+++ b/lib/refinement.js
@@ -26,7 +26,9 @@ function refinement(type, predicate, name) {
   function Refinement(value, path) {
 
     if (process.env.NODE_ENV !== 'production') {
-      forbidNewOperator(this, Refinement);
+      if (identity) {
+        forbidNewOperator(this, Refinement);
+      }
       path = path || [displayName];
     }
 
diff --git a/lib/union.js b/lib/union.js
index a5636ec..bc640f7 100644
--- a/lib/union.js
+++ b/lib/union.js
@@ -39,7 +39,9 @@ function union(types, name) {
     }
 
     if (process.env.NODE_ENV !== 'production') {
-      forbidNewOperator(this, Union);
+      if (identity) {
+        forbidNewOperator(this, Union);
+      }
       path = path || [displayName];
       assert(isType(type), function () { return 'Invalid value ' + assert.stringify(value) + ' supplied to ' + path.join('/') + ' (no constructor returned by dispatch)'; });
       path[path.length - 1] += '(' + getTypeName(type) + ')';
diff --git a/package.json b/package.json
index ffb101a..81a396a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "tcomb",
-  "version": "3.2.4",
+  "version": "3.2.5",
   "description": "Type checking and DDD for JavaScript",
   "main": "index.js",
   "typings": "index.d.ts",


Test patch:
diff --git a/test/declare.js b/test/declare.js
index 1dbe44f..efac3c7 100644
--- a/test/declare.js
+++ b/test/declare.js
@@ -2,6 +2,7 @@
 var assert = require('assert');
 var t = require('../index');
 var throwsWithMessage = require('./util').throwsWithMessage;
+var create = require('../lib/create');
 
 var A = t.declare('A');
 
@@ -101,7 +102,7 @@ describe('t.declare([name])', function () {
       assert.equal(Tuple.meta && Tuple.meta.identity, false);
       assert.equal(Result.meta && Result.meta.identity, false);
       Tuple.define(t.tuple([t.String]));
-      assert.equal(Tuple.meta && Tuple.meta.identity, false);
+      assert.equal(Tuple.meta && Tuple.meta.identity, true);
       assert.equal(Result.meta && Result.meta.identity, false);
 
       Tuple = t.declare('Tuple');
@@ -113,6 +114,16 @@ describe('t.declare([name])', function () {
       assert.equal(Result.meta && Result.meta.identity, false);
     });
 
+    it('should play well with enums', function () {
+      var A = t.declare('A');
+      A.define(t.enums.of(['a']));
+      assert.strictEqual(create(A, 'a'), 'a');
+
+      var B = t.declare('B');
+      B.define(t.refinement(t.String, function () { return true; }));
+      assert.strictEqual(create(B, 'a string'), 'a string');
+    });
+
   });
 
   describe('constructor', function () {