Repository: gcanti/tcomb (js)

Issue:
RFC: Relax `isObject` contraint in interface combinator
```js
import t from 'tcomb'

const ToStringable = t.interface({
  toString: t.Function
})

console.log(ToStringable.is(1)) // => false, should be true
```

- https://github.com/gcanti/tcomb/blob/master/lib/interface.js#L55
- https://github.com/gcanti/tcomb/blob/master/lib/interface.js#L97

/cc @ctrlplusb @giogonzo

Pull request description:
relax `isObject` contraint in interface combinator, fix #214

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index dbef166..5b51257 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.2
+
+- **Bug Fix**
+  - relax `isObject` contraint in interface combinator, fix #214
+
 # v3.2.1
 
 - **Bug Fix**
diff --git a/lib/interface.js b/lib/interface.js
index 8f5c50d..dd529c5 100644
--- a/lib/interface.js
+++ b/lib/interface.js
@@ -52,10 +52,9 @@ function inter(props, options) {
 
     if (process.env.NODE_ENV !== 'production') {
       path = path || [displayName];
-      assert(isObject(value), function () { return 'Invalid value ' + assert.stringify(value) + ' supplied to ' + path.join('/') + ' (expected an object)'; });
       // strictness
       if (strict) {
-        for (k in value) {
+        for (var k in value) {
           assert(props.hasOwnProperty(k), function () { return 'Invalid additional prop "' + k + '" supplied to ' + path.join('/'); });
         }
       }
@@ -63,12 +62,12 @@ function inter(props, options) {
 
     var idempotent = true;
     var ret = {};
-    for (var k in props) {
-      var expected = props[k];
-      var actual = value[k];
-      var instance = create(expected, actual, ( process.env.NODE_ENV !== 'production' ? path.concat(k + ': ' + getTypeName(expected)) : null ));
+    for (var prop in props) {
+      var expected = props[prop];
+      var actual = value[prop];
+      var instance = create(expected, actual, ( process.env.NODE_ENV !== 'production' ? path.concat(prop + ': ' + getTypeName(expected)) : null ));
       idempotent = idempotent && ( actual === instance );
-      ret[k] = instance;
+      ret[prop] = instance;
     }
 
     if (idempotent) { // implements idempotency
@@ -94,18 +93,15 @@ function inter(props, options) {
   Interface.displayName = displayName;
 
   Interface.is = function (x) {
-    if (!isObject(x)) {
-      return false;
-    }
     if (strict) {
-      for (k in x) {
+      for (var k in x) {
         if (!props.hasOwnProperty(k)) {
           return false;
         }
       }
     }
-    for (var k in props) {
-      if (!is(x[k], props[k])) {
+    for (var prop in props) {
+      if (!is(x[prop], props[prop])) {
         return false;
       }
     }
diff --git a/package.json b/package.json
index 4fe4e21..ab179d7 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "tcomb",
-  "version": "3.2.1",
+  "version": "3.2.2",
   "description": "Type checking and DDD for JavaScript",
   "main": "index.js",
   "typings": "index.d.ts",


Test patch:
diff --git a/test/interface.js b/test/interface.js
index ca9ced4..20f6d50 100644
--- a/test/interface.js
+++ b/test/interface.js
@@ -13,6 +13,10 @@ var PointInterface = t.inter({
   y: t.Number
 });
 
+var ToStringable = t.inter({
+  toString: t.Function
+});
+
 var HydrateInterface = t.inter({point: Point});
 
 describe('t.interface(props, [name])', function () {
@@ -73,7 +77,7 @@ describe('t.interface(props, [name])', function () {
     it('should accept only valid values', function () {
       throwsWithMessage(function () {
         PointInterface(1);
-      }, '[tcomb] Invalid value 1 supplied to {x: Number, y: Number} (expected an object)');
+      }, '[tcomb] Invalid value undefined supplied to {x: Number, y: Number}/x: Number');
       throwsWithMessage(function () {
         PointInterface({});
       }, '[tcomb] Invalid value undefined supplied to {x: Number, y: Number}/x: Number');
@@ -149,6 +153,9 @@ describe('t.interface(props, [name])', function () {
 
     it('should check types', function () {
       assert.equal(PointInterface.is({ x: 1, y: 'a' }), false);
+      assert.equal(PointInterface.is(1), false);
+      assert.equal(ToStringable.is(1), true);
+      assert.equal(ToStringable.is({}), true);
     });
 
     it('should allow additional props', function () {